/*-------------------------------------------------------------------------
 *
 * extension.c
 *	  操作扩展的命令
 *
 * PostgreSQL 中的扩展允许管理 SQL 对象的集合。
 *
 * 我们需要的只是一个 OID，以便能够将相关对象与之关联。  通过从 "控制" 文件填充 pg_extension 目录来创建扩展。 扩展控制文件使用与我们用于 postgresql.conf 相同的解析器进行解析。 扩展还具有安装脚本文件，包含创建扩展对象的 SQL 命令。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/extension.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <dirent.h>
#include <limits.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "storage/fd.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/varlena.h"


/* 全局可见的状态变量 */
bool		creating_extension = false;
Oid			CurrentExtensionObject = InvalidOid;

/*
 * 内部数据结构，用于保存解析控制文件的结果
 */
typedef struct ExtensionControlFile
{
	char	   *name;			/* 扩展名 */
	char	   *directory;		/* 脚本文件的目录 */
	char	   *default_version;	/* 默认安装目标版本（如果有的话） */
	char	   *module_pathname;	/* 用于替代
									 * MODULE_PATHNAME 的字符串 */
	char	   *comment;		/* 注释（如果有的话） */
	char	   *schema;			/* 目标模式（如果 !relocatable 则允许） */
	bool		relocatable;	/* 是否支持 ALTER EXTENSION SET SCHEMA？ */
	bool		superuser;		/* 是否必须是超级用户才能安装？ */
	bool		trusted;		/* 是否允许即时成为超级用户？ */
	int			encoding;		/* 脚本文件的编码，或 -1 */
	List	   *requires;		/* 先决条件扩展的名称 */
} ExtensionControlFile;

/*
 * 用于更新路径信息的内部数据结构
 */
typedef struct ExtensionVersionInfo
{
	char	   *name;			/* 起始版本的名称 */
	List	   *reachable;		/* ExtensionVersionInfo 的列表 */
	bool		installable;	/* 此版本是否有安装脚本？ */
	/* Dijkstra 算法的工作状态： */
	bool		distance_known; /* 从起点到此的距离是否已知？ */
	int			distance;		/* 当前最坏情况的距离估计 */
	struct ExtensionVersionInfo *previous;	/* 当前最佳前驱 */
} ExtensionVersionInfo;

/* 本地函数 */
static List *fc_find_update_path(List *fc_evi_list,
							  ExtensionVersionInfo *fc_evi_start,
							  ExtensionVersionInfo *fc_evi_target,
							  bool fc_reject_indirect,
							  bool fc_reinitialize);
static Oid	fc_get_required_extension(char *fc_reqExtensionName,
								   char *fc_extensionName,
								   char *fc_origSchemaName,
								   bool fc_cascade,
								   List *fc_parents,
								   bool fc_is_create);
static void fc_get_available_versions_for_extension(ExtensionControlFile *fc_pcontrol,
												 Tuplestorestate *fc_tupstore,
												 TupleDesc fc_tupdesc);
static Datum fc_convert_requires_to_datum(List *fc_requires);
static void fc_ApplyExtensionUpdates(Oid fc_extensionOid,
								  ExtensionControlFile *fc_pcontrol,
								  const char *fc_initialVersion,
								  List *fc_updateVersions,
								  char *fc_origSchemaName,
								  bool fc_cascade,
								  bool fc_is_create);
static char *fc_read_whole_file(const char *fc_filename, int *fc_length);


/*
 * get_extension_oid - 给定扩展名称，查找 OID
 *
 * 如果 missing_ok 为 false，则在找不到扩展名称时抛出错误。如果
 * 为 true，则只返回 InvalidOid。
 */
Oid get_extension_oid(const char *fc_extname, bool fc_missing_ok)
{
	Oid			fc_result;
	Relation	fc_rel;
	SysScanDesc fc_scandesc;
	HeapTuple	fc_tuple;
	ScanKeyData fc_entry[1];

	fc_rel = table_open(ExtensionRelationId, AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_extension_extname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_extname));

	fc_scandesc = systable_beginscan(fc_rel, ExtensionNameIndexId, true,
								  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scandesc);

	/* 我们假设最多只有一个匹配的元组 */
	if (HeapTupleIsValid(fc_tuple))
		fc_result = ((Form_pg_extension) GETSTRUCT(fc_tuple))->oid;
	else
		fc_result = InvalidOid;

	systable_endscan(fc_scandesc);

	table_close(fc_rel, AccessShareLock);

	if (!OidIsValid(fc_result) && !fc_missing_ok)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("extension \"%s\" does not exist",
						fc_extname)));

	return fc_result;
}

/*
 * get_extension_name - 给定扩展 OID，查找名称
 *
 * 返回一个 palloc'd 字符串，如果没有这样的扩展则返回 NULL。
 */
char * get_extension_name(Oid fc_ext_oid)
{
	char	   *fc_result;
	Relation	fc_rel;
	SysScanDesc fc_scandesc;
	HeapTuple	fc_tuple;
	ScanKeyData fc_entry[1];

	fc_rel = table_open(ExtensionRelationId, AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_ext_oid));

	fc_scandesc = systable_beginscan(fc_rel, ExtensionOidIndexId, true,
								  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scandesc);

	/* 我们假设最多只有一个匹配的元组 */
	if (HeapTupleIsValid(fc_tuple))
		fc_result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(fc_tuple))->extname));
	else
		fc_result = NULL;

	systable_endscan(fc_scandesc);

	table_close(fc_rel, AccessShareLock);

	return fc_result;
}

/*
 * get_extension_schema - 给定扩展 OID，获取其 extnamespace
 *
 * 如果没有这样的扩展，则返回 InvalidOid。
 */
static Oid fc_get_extension_schema(Oid fc_ext_oid)
{
	Oid			fc_result;
	Relation	fc_rel;
	SysScanDesc fc_scandesc;
	HeapTuple	fc_tuple;
	ScanKeyData fc_entry[1];

	fc_rel = table_open(ExtensionRelationId, AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_ext_oid));

	fc_scandesc = systable_beginscan(fc_rel, ExtensionOidIndexId, true,
								  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scandesc);

	/* 我们假设最多只有一个匹配的元组 */
	if (HeapTupleIsValid(fc_tuple))
		fc_result = ((Form_pg_extension) GETSTRUCT(fc_tuple))->extnamespace;
	else
		fc_result = InvalidOid;

	systable_endscan(fc_scandesc);

	table_close(fc_rel, AccessShareLock);

	return fc_result;
}

/*
 * 检查扩展和版本名称有效性的实用函数
 */
static void fc_check_valid_extension_name(const char *fc_extensionname)
{
	int			fc_namelen = strlen(fc_extensionname);

	/*
	 * 不允许空名称（解析器会拒绝空标识符，但
	 * 让我们检查一下）。
	 */
	if (fc_namelen == 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension name: \"%s\"", fc_extensionname),
				 errdetail("Extension names must not be empty.")));

	/*
	 * 不允许双破折号，因为这会使脚本文件名模糊。
	 */
	if (strstr(fc_extensionname, "--"))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension name: \"%s\"", fc_extensionname),
				 errdetail("Extension names must not contain \"--\".")));

	/*
	 * 也不允许前导或尾随破折号。（我们可能可以允许这一点，但
	 * 这需要在文件名解析中格外小心，并且会使文件名
	 * 在视觉上或至少在形式上变得模糊。由于没有现实世界的使用
	 * 案例，让我们禁止它。）
	 */
	if (fc_extensionname[0] == '-' || fc_extensionname[fc_namelen - 1] == '-')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension name: \"%s\"", fc_extensionname),
				 errdetail("Extension names must not begin or end with \"-\".")));

	/*
	 * 也不允许目录分隔符（这足以防止 ".."
	 * 风格的攻击）。
	 */
	if (first_dir_separator(fc_extensionname) != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension name: \"%s\"", fc_extensionname),
				 errdetail("Extension names must not contain directory separator characters.")));
}

static void fc_check_valid_version_name(const char *fc_versionname)
{
#ifdef FDD //cppcheck
	int			fc_namelen = 0;
	if (!fc_versionname)
	{
		//cppcheck
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension version name"),
				 errdetail("Version names must not be empty.")));
		return;
	}

	fc_namelen = strlen(fc_versionname);
#else
	int			fc_namelen = strlen(fc_versionname);
#endif
	/*
	 * 不允许空名称（我们可能可以允许这一点，但似乎
	 * 没有太大意义）。
	 */
	if (fc_namelen == 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension version name: \"%s\"", fc_versionname),
				 errdetail("Version names must not be empty.")));

	/*
	 * 不允许双破折号，因为这会使脚本文件名模糊。
	 */
	if (strstr(fc_versionname, "--"))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension version name: \"%s\"", fc_versionname),
				 errdetail("Version names must not contain \"--\".")));

	/*
	 * 也不允许前导或尾随破折号。
	 */
	if (fc_versionname[0] == '-' || fc_versionname[fc_namelen - 1] == '-')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension version name: \"%s\"", fc_versionname),
				 errdetail("Version names must not begin or end with \"-\".")));

	/*
	 * 也不允许目录分隔符（这足以防止 ".."
	 * 风格的攻击）。
	 */
	if (first_dir_separator(fc_versionname) != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid extension version name: \"%s\"", fc_versionname),
				 errdetail("Version names must not contain directory separator characters.")));
}

/*
 * 处理与扩展相关的路径名称的实用函数
 */
static bool fc_is_extension_control_filename(const char *fc_filename)
{
	const char *fc_extension = strrchr(fc_filename, '.');

	return (fc_extension != NULL) && (strcmp(fc_extension, ".control") == 0);
}

static bool fc_is_extension_script_filename(const char *fc_filename)
{
	const char *fc_extension = strrchr(fc_filename, '.');

	return (fc_extension != NULL) && (strcmp(fc_extension, ".sql") == 0);
}

static char * fc_get_extension_control_directory(void)
{
	char		fc_sharepath[MAXPGPATH];
	char	   *fc_result;

	get_share_path(my_exec_path, fc_sharepath);
	fc_result = (char *) palloc(MAXPGPATH);
	snprintf(fc_result, MAXPGPATH, "%s/extension", fc_sharepath);

	return fc_result;
}

static char * fc_get_extension_control_filename(const char *fc_extname)
{
	char		fc_sharepath[MAXPGPATH];
	char	   *fc_result;

	get_share_path(my_exec_path, fc_sharepath);
	fc_result = (char *) palloc(MAXPGPATH);
	snprintf(fc_result, MAXPGPATH, "%s/extension/%s.control",
			 fc_sharepath, fc_extname);

	return fc_result;
}

static char * fc_get_extension_script_directory(ExtensionControlFile *fc_control)
{
	char		fc_sharepath[MAXPGPATH];
	char	   *fc_result;

	/*
	 * 目录参数可以省略、绝对或相对于
	 * 安装的共享目录。
	 */
	if (!fc_control->directory)
		return fc_get_extension_control_directory();

	if (is_absolute_path(fc_control->directory))
		return pstrdup(fc_control->directory);

	get_share_path(my_exec_path, fc_sharepath);
	fc_result = (char *) palloc(MAXPGPATH);
	snprintf(fc_result, MAXPGPATH, "%s/%s", fc_sharepath, fc_control->directory);

	return fc_result;
}

static char * fc_get_extension_aux_control_filename(ExtensionControlFile *fc_control,
								   const char *fc_version)
{
	char	   *fc_result;
	char	   *fc_scriptdir;

	fc_scriptdir = fc_get_extension_script_directory(fc_control);

	fc_result = (char *) palloc(MAXPGPATH);
	snprintf(fc_result, MAXPGPATH, "%s/%s--%s.control",
			 fc_scriptdir, fc_control->name, fc_version);

	pfree(fc_scriptdir);

	return fc_result;
}

static char * fc_get_extension_script_filename(ExtensionControlFile *fc_control,
							  const char *fc_from_version, const char *fc_version)
{
	char	   *fc_result;
	char	   *fc_scriptdir;

	fc_scriptdir = fc_get_extension_script_directory(fc_control);

	fc_result = (char *) palloc(MAXPGPATH);
	if (fc_from_version)
		snprintf(fc_result, MAXPGPATH, "%s/%s--%s--%s.sql",
				 fc_scriptdir, fc_control->name, fc_from_version, fc_version);
	else
		snprintf(fc_result, MAXPGPATH, "%s/%s--%s.sql",
				 fc_scriptdir, fc_control->name, fc_version);

	pfree(fc_scriptdir);

	return fc_result;
}


/*
 * 解析主控制文件或辅助控制文件的内容，并填充
 * *control的字段。如果版本为NULL，则解析主文件，
 * 否则解析该版本的可选辅助文件。
 *
 * 控制文件应非常简短，仅有六行左右，
 * 所以我们在这里不担心内存分配风险。此外，我们也不
 * 担心它的编码；所有值都应为ASCII。
 */
static void fc_parse_extension_control_file(ExtensionControlFile *fc_control,
							 const char *fc_version)
{
	char	   *fc_filename;
	FILE	   *fc_file;
	ConfigVariable *fc_item,
			   *fc_head = NULL,
			   *fc_tail = NULL;

	/*
	 * 定位要读取的文件。辅助文件是可选的。
	 */
	if (fc_version)
		fc_filename = fc_get_extension_aux_control_filename(fc_control, fc_version);
	else
		fc_filename = fc_get_extension_control_filename(fc_control->name);

	if ((fc_file = AllocateFile(fc_filename, "r")) == NULL)
	{
		if (errno == ENOENT)
		{
			/* 对缺失的辅助文件没有抱怨 */
			if (fc_version)
			{
				pfree(fc_filename);
				return;
			}

			/* 缺失控制文件表示扩展未安装 */
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("extension \"%s\" is not available", fc_control->name),
					 errdetail("Could not open extension control file \"%s\": %m.",
							   fc_filename),
					 errhint("The extension must first be installed on the system where PostgreSQL is running.")));
		}
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open extension control file \"%s\": %m",
						fc_filename)));
	}

	/*
	 * 使用GUC的文件解析代码解析文件内容。我们无需
	 * 检查返回值，因为任何错误都会在ERROR级别抛出。
	 */
	(void) ParseConfigFp(fc_file, fc_filename, 0, ERROR, &fc_head, &fc_tail);

	FreeFile(fc_file);

	/*
	 * 将ConfigVariable列表转换为ExtensionControlFile条目。
	 */
	for (fc_item = fc_head; fc_item != NULL; fc_item = fc_item->next)
	{
		if (strcmp(fc_item->name, "directory") == 0)
		{
			if (fc_version)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
								fc_item->name)));

			fc_control->directory = pstrdup(fc_item->value);
		}
		else if (strcmp(fc_item->name, "default_version") == 0)
		{
			if (fc_version)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
								fc_item->name)));

			fc_control->default_version = pstrdup(fc_item->value);
		}
		else if (strcmp(fc_item->name, "module_pathname") == 0)
		{
			fc_control->module_pathname = pstrdup(fc_item->value);
		}
		else if (strcmp(fc_item->name, "comment") == 0)
		{
			fc_control->comment = pstrdup(fc_item->value);
		}
		else if (strcmp(fc_item->name, "schema") == 0)
		{
			fc_control->schema = pstrdup(fc_item->value);
		}
		else if (strcmp(fc_item->name, "relocatable") == 0)
		{
			if (!parse_bool(fc_item->value, &fc_control->relocatable))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("parameter \"%s\" requires a Boolean value",
								fc_item->name)));
		}
		else if (strcmp(fc_item->name, "superuser") == 0)
		{
			if (!parse_bool(fc_item->value, &fc_control->superuser))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("parameter \"%s\" requires a Boolean value",
								fc_item->name)));
		}
		else if (strcmp(fc_item->name, "trusted") == 0)
		{
			if (!parse_bool(fc_item->value, &fc_control->trusted))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("parameter \"%s\" requires a Boolean value",
								fc_item->name)));
		}
		else if (strcmp(fc_item->name, "encoding") == 0)
		{
			fc_control->encoding = pg_valid_server_encoding(fc_item->value);
			if (fc_control->encoding < 0)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("\"%s\" is not a valid encoding name",
								fc_item->value)));
		}
		else if (strcmp(fc_item->name, "requires") == 0)
		{
			/* 需要可修改的字符串副本 */
			char	   *fc_rawnames = pstrdup(fc_item->value);

			/* 将字符串解析为标识符列表 */
			if (!SplitIdentifierString(fc_rawnames, ',', &fc_control->requires))
			{
				/* 名称列表中的语法错误 */
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("parameter \"%s\" must be a list of extension names",
								fc_item->name)));
			}
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
							fc_item->name, fc_filename)));
	}

	FreeConfigVariables(fc_head);

	if (fc_control->relocatable && fc_control->schema != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));

	pfree(fc_filename);
}

/*
 * 读取指定扩展的主控制文件。
 */
static ExtensionControlFile *
fc_read_extension_control_file(const char *fc_extname)
{
	ExtensionControlFile *fc_control;

	/*
	 * 设置默认值。指针字段最初为null。
	 */
	fc_control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
	fc_control->name = pstrdup(fc_extname);
	fc_control->relocatable = false;
	fc_control->superuser = true;
	fc_control->trusted = false;
	fc_control->encoding = -1;

	/*
	 * 解析主控制文件。
	 */
	fc_parse_extension_control_file(fc_control, NULL);

	return fc_control;
}

/*
 * 读取指定扩展和版本的辅助控制文件。
 *
 * 返回一个新的修改后的ExtensionControlFile结构；
 * 原始结构（仅反映主控制文件）没有被修改。
 */
static ExtensionControlFile *
fc_read_extension_aux_control_file(const ExtensionControlFile *fc_pcontrol,
								const char *fc_version)
{
	ExtensionControlFile *fc_acontrol;

	/*
	 * 扁平复制结构。指针字段与原始结构共享值。
	 */
	fc_acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
	memcpy(fc_acontrol, fc_pcontrol, sizeof(ExtensionControlFile));

	/*
	 * 解析辅助控制文件，覆盖结构字段
	 */
	fc_parse_extension_control_file(fc_acontrol, fc_version);

	return fc_acontrol;
}

/*
 * 将SQL脚本文件读取到字符串中，并转换为数据库编码
 */
static char * fc_read_extension_script_file(const ExtensionControlFile *fc_control,
						   const char *fc_filename)
{
	int			fc_src_encoding;
	char	   *fc_src_str;
	char	   *fc_dest_str;
	int			fc_len;

	fc_src_str = fc_read_whole_file(fc_filename, &fc_len);

	/* 如果未给出则使用数据库编码 */
	if (fc_control->encoding < 0)
		fc_src_encoding = GetDatabaseEncoding();
	else
		fc_src_encoding = fc_control->encoding;

	/* 确保源字符串在预期编码中有效 */
	(void) pg_verify_mbstr(fc_src_encoding, fc_src_str, fc_len, false);

	/*
	 * 将编码转换为数据库编码。read_whole_file
	 * 将字符串以null结束，因此如果没有转换，字符串本身是有效的。
	 */
	fc_dest_str = pg_any_to_server(fc_src_str, fc_len, fc_src_encoding);

	return fc_dest_str;
}

/*
 * 执行给定的SQL字符串。
 *
 * 注意：诱惑让人只使用SPI来执行字符串，但这不
 * 太有效。真正严重的问题是SPI会在执行任何内容之前解析、
 * 分析并计划整条字符串；当然，如果有任何计划语句引用了
 * 脚本中早先创建的对象，这将失败。一个较小的烦恼是SPI坚持
 * 在任何错误发生时将整条字符串作为错误上下文打印，这
 * 可能非常冗长。
 */
static void fc_execute_sql_string(const char *fc_sql)
{
	List	   *fc_raw_parsetree_list;
	DestReceiver *fc_dest;
	ListCell   *fc_lc1;

	/*
	 * 将SQL字符串解析为一组原始解析树。
	 */
	fc_raw_parsetree_list = pg_parse_query(fc_sql);

	/* 所有SELECT的输出都进入位桶 */
	fc_dest = CreateDestReceiver(DestNone);

	/*
	 * 对每个原始解析树进行解析分析、规则重写、计划和执行。
	 * 在开始对下一个进行解析分析之前，我们必须完全执行
	 * 每个查询，因为可能存在相互依赖关系。
	 */
	foreach(fc_lc1, fc_raw_parsetree_list)
	{
		RawStmt    *fc_parsetree = lfirst_node(RawStmt, fc_lc1);
		MemoryContext fc_per_parsetree_context,
					fc_oldcontext;
		List	   *fc_stmt_list;
		ListCell   *fc_lc2;

		/*
		 * 我们在一个短期上下文中为每个解析树进行处理，以
		 * 限制字符串中多个命令时使用的内存。
		 */
		fc_per_parsetree_context =
			AllocSetContextCreate(CurrentMemoryContext,
								  "execute_sql_string per-statement context",
								  ALLOCSET_DEFAULT_SIZES);
		fc_oldcontext = MemoryContextSwitchTo(fc_per_parsetree_context);

		/* 确保解析器可以看到迄今为止完成的任何 DDL */
		CommandCounterIncrement();

		fc_stmt_list = pg_analyze_and_rewrite_fixedparams(fc_parsetree,
													   fc_sql,
													   NULL,
													   0,
													   NULL);
		fc_stmt_list = pg_plan_queries(fc_stmt_list, fc_sql, CURSOR_OPT_PARALLEL_OK, NULL);

		foreach(fc_lc2, fc_stmt_list)
		{
			PlannedStmt *fc_stmt = lfirst_node(PlannedStmt, fc_lc2);

			CommandCounterIncrement();

			PushActiveSnapshot(GetTransactionSnapshot());

			if (fc_stmt->utilityStmt == NULL)
			{
				QueryDesc  *fc_qdesc;

				fc_qdesc = CreateQueryDesc(fc_stmt,
										fc_sql,
										GetActiveSnapshot(), NULL,
										fc_dest, NULL, NULL, 0);

				ExecutorStart(fc_qdesc, 0);
				ExecutorRun(fc_qdesc, ForwardScanDirection, 0, true);
				ExecutorFinish(fc_qdesc);
				ExecutorEnd(fc_qdesc);

				FreeQueryDesc(fc_qdesc);
			}
			else
			{
				if (IsA(fc_stmt->utilityStmt, TransactionStmt))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("transaction control statements are not allowed within an extension script")));

				ProcessUtility(fc_stmt,
							   fc_sql,
							   false,
							   PROCESS_UTILITY_QUERY,
							   NULL,
							   NULL,
							   fc_dest,
							   NULL);
			}

			PopActiveSnapshot();
		}

		/* 清理每个解析树上下文。 */
		MemoryContextSwitchTo(fc_oldcontext);
		MemoryContextDelete(fc_per_parsetree_context);
	}

	/* 确保在最后一个脚本命令后推进命令计数器 */
	CommandCounterIncrement();
}

/*
 * 策略函数：给定扩展是否被非超级用户信任进行安装？
 *
 * （如果您更改此内容，请更新下面的 errhint 逻辑。）
 */
static bool fc_extension_is_trusted(ExtensionControlFile *fc_control)
{
	AclResult	fc_aclresult;

	/* 除非扩展的控制文件说明可以信任，否则永远不要信任 */
	if (!fc_control->trusted)
		return false;
	/* 如果用户在当前数据库上具有 CREATE 权限，则允许 */
	fc_aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
	if (fc_aclresult == ACLCHECK_OK)
		return true;
	return false;
}

/*
 * 执行适当的脚本文件以安装或更新扩展
 *
 * 如果 from_version 不为 NULL，则为更新
 */
static void fc_execute_extension_script(Oid fc_extensionOid, ExtensionControlFile *fc_control,
						 const char *fc_from_version,
						 const char *fc_version,
						 List *fc_requiredSchemas,
						 const char *fc_schemaName, Oid fc_schemaOid)
{
	bool		fc_switch_to_superuser = false;
	char	   *fc_filename;
	Oid			fc_save_userid = 0;
	int			fc_save_sec_context = 0;
	int			fc_save_nestlevel;
	StringInfoData fc_pathbuf;
	ListCell   *fc_lc;

	/*
	 * 如果适当，强制超级用户特权。我们将这些检查推迟到
	 * 这里，以便控制标志与正确的脚本（如果它们恰好在次级控制文件中设置）正确关联。
	 */
	if (fc_control->superuser && !superuser())
	{
		if (fc_extension_is_trusted(fc_control))
			fc_switch_to_superuser = true;
		else if (fc_from_version == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to create extension \"%s\"",
							fc_control->name),
					 fc_control->trusted
					 ? errhint("Must have CREATE privilege on current database to create this extension.")
					 : errhint("Must be superuser to create this extension.")));
		else
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to update extension \"%s\"",
							fc_control->name),
					 fc_control->trusted
					 ? errhint("Must have CREATE privilege on current database to update this extension.")
					 : errhint("Must be superuser to update this extension.")));
	}

	fc_filename = fc_get_extension_script_filename(fc_control, fc_from_version, fc_version);

	/*
	 * 如果代表非超级用户安装受信任的扩展，则成为
	 * 启动超级用户。（如果事务中止，这个切换将自动清理，
	 * 下面的 GUC 更改也是。）
	 */
	if (fc_switch_to_superuser)
	{
		GetUserIdAndSecContext(&fc_save_userid, &fc_save_sec_context);
		SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
							   fc_save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
	}

	/*
	 * 强制 client_min_messages 和 log_min_messages 至少为 WARNING，
	 * 以便我们不会因常见脚本操作（如创建 shell 类型）而向用户发送无用的 NOTICE 消息。
	 *
	 * 我们使用相当于函数 SET 选项的方法，以允许该设置
	 * 持续到脚本执行的确切持续时间。guc.c 还
	 * 负责在错误时撤销该设置。
	 *
	 * log_min_messages 不能被普通用户设置，因此对于那个，我们
	 * 假装是超级用户。
	 */
	fc_save_nestlevel = NewGUCNestLevel();

	if (client_min_messages < WARNING)
		(void) set_config_option("client_min_messages", "warning",
								 PGC_USERSET, PGC_S_SESSION,
								 GUC_ACTION_SAVE, true, 0, false);
	if (log_min_messages < WARNING)
		(void) set_config_option_ext("log_min_messages", "warning",
									 PGC_SUSET, PGC_S_SESSION,
									 BOOTSTRAP_SUPERUSERID,
									 GUC_ACTION_SAVE, true, 0, false);

	/*
	 * 同样禁用 check_function_bodies，以确保 SQL 函数
	 * 在创建期间不会被解析。
	 */
	if (check_function_bodies)
		(void) set_config_option("check_function_bodies", "off",
								 PGC_USERSET, PGC_S_SESSION,
								 GUC_ACTION_SAVE, true, 0, false);

	/*
	 * 设置搜索路径，使目标模式优先，使其成为
	 * 默认创建目标名称空间。然后添加任何
	 * 先决扩展的模式，除非它们在 pg_catalog 中，这样就会
	 * 被搜索到。（在非首位位置显式列出 pg_catalog
	 * 对安全性是不好的。）最后添加 pg_temp 以确保
	 * 临时对象不会优先于其他对象。
	 *
	 * 注意：使用 PushOverrideSearchPath 似乎很诱人，
	 * 但我们不能这样做。我们必须实际设置 search_path GUC，以便
	 * 扩展脚本可以检查或更改它。无论如何，
	 * GUC_ACTION_SAVE 方法同样方便。
	 */
	initStringInfo(&fc_pathbuf);
	appendStringInfoString(&fc_pathbuf, quote_identifier(fc_schemaName));
	foreach(fc_lc, fc_requiredSchemas)
	{
		Oid			fc_reqschema = lfirst_oid(fc_lc);
		char	   *fc_reqname = get_namespace_name(fc_reqschema);

		if (fc_reqname && strcmp(fc_reqname, "pg_catalog") != 0)
			appendStringInfo(&fc_pathbuf, ", %s", quote_identifier(fc_reqname));
	}
	appendStringInfoString(&fc_pathbuf, ", pg_temp");

	(void) set_config_option("search_path", fc_pathbuf.data,
							 PGC_USERSET, PGC_S_SESSION,
							 GUC_ACTION_SAVE, true, 0, false);

	/*
	 * 设置 creating_extension 和相关变量，以便
	 * recordDependencyOnCurrentExtension 和其他函数执行正确
	 * 的操作。在失败时，确保我们重置这些变量。
	 */
	creating_extension = true;
	CurrentExtensionObject = fc_extensionOid;
	PG_TRY();
	{
		char	   *fc_c_sql = fc_read_extension_script_file(fc_control, fc_filename);
		Datum		fc_t_sql;

		/*
		 * 我们通过 quote_identifier() 过滤每个替换。当参数包含以下字符之一时，任何一种引用集合在 $$dollar-quoted string literals$$、'single-quoted string literals' 和任何文字外都无法正常工作。为了避免对扩展作者的安全陷阱，对于包含这些字符的参数替换时会报错。
		 */
		const char *fc_quoting_relevant_chars = "\"$'\\";

		/* 我们使用各种函数来处理文本数据 */
		fc_t_sql = CStringGetTextDatum(fc_c_sql);

		/*
		 * 将任何以 "\echo" 开头的行减少为空。这样可以允许脚本包含提示人们不要通过 psql 运行它们的消息，这被发现是由于旧习惯所必需的。
		 */
		fc_t_sql = DirectFunctionCall4Coll(textregexreplace,
										C_COLLATION_OID,
										fc_t_sql,
										CStringGetTextDatum("^\\\\echo.*$"),
										CStringGetTextDatum(""),
										CStringGetTextDatum("ng"));

		/*
		 * 如果脚本使用 @extowner@，则替换为当前调用的用户名。
		 */
		if (strstr(fc_c_sql, "@extowner@"))
		{
			Oid			fc_uid = fc_switch_to_superuser ? fc_save_userid : GetUserId();
			const char *fc_userName = GetUserNameFromId(fc_uid, false);
			const char *fc_qUserName = quote_identifier(fc_userName);

			fc_t_sql = DirectFunctionCall3Coll(replace_text,
											C_COLLATION_OID,
											fc_t_sql,
											CStringGetTextDatum("@extowner@"),
											CStringGetTextDatum(fc_qUserName));
			if (strpbrk(fc_userName, fc_quoting_relevant_chars))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid character in extension owner: must not contain any of \"%s\"",
								fc_quoting_relevant_chars)));
		}

		/*
		 * 如果它不可重定位，则用 @extschema@ 的出现处替换目标模式名称。
		 *
		 * 对于可重定位的扩展，我们不需要这样做。不能有需要 @extschema@ 的情况，否则它就不会是可重定位的。
		 */
		if (!fc_control->relocatable)
		{
			Datum		fc_old = fc_t_sql;
			const char *fc_qSchemaName = quote_identifier(fc_schemaName);

			fc_t_sql = DirectFunctionCall3Coll(replace_text,
											C_COLLATION_OID,
											fc_t_sql,
											CStringGetTextDatum("@extschema@"),
											CStringGetTextDatum(fc_qSchemaName));
			if (fc_t_sql != fc_old && strpbrk(fc_schemaName, fc_quoting_relevant_chars))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
								fc_control->name, fc_quoting_relevant_chars)));
		}

		/*
		 * 如果在控制文件中设置了 module_pathname，则用 MODULE_PATHNAME 的出现处替换其值。
		 */
		if (fc_control->module_pathname)
		{
			fc_t_sql = DirectFunctionCall3Coll(replace_text,
											C_COLLATION_OID,
											fc_t_sql,
											CStringGetTextDatum("MODULE_PATHNAME"),
											CStringGetTextDatum(fc_control->module_pathname));
		}

		/* 现在重新回到 C 字符串 */
		fc_c_sql = text_to_cstring(DatumGetTextPP(fc_t_sql));

		fc_execute_sql_string(fc_c_sql);
	}
	PG_FINALLY();
	{
		creating_extension = false;
		CurrentExtensionObject = InvalidOid;
	}
	PG_END_TRY();

	/*
	 * 恢复我们之前设置的 GUC 变量。
	 */
	AtEOXact_GUC(true, fc_save_nestlevel);

	/*
	 * 如果需要，恢复身份验证状态。
	 */
	if (fc_switch_to_superuser)
		SetUserIdAndSecContext(fc_save_userid, fc_save_sec_context);
}

/*
 * 查找或创建指定版本名称的 ExtensionVersionInfo
 *
 * 当前，我们仅使用 ExtensionVersionInfo 的列表。因此，在有 N 个版本的扩展时，搜索它们大约需要 O(N^2) 时间。如果这成为瓶颈，我们可以将数据结构更改为哈希表。
 */
static ExtensionVersionInfo * fc_get_ext_ver_info(const char *fc_versionname, List **fc_evi_list)
{
	ExtensionVersionInfo *fc_evi;
	ListCell   *fc_lc;

	foreach(fc_lc, *fc_evi_list)
	{
		fc_evi = (ExtensionVersionInfo *) lfirst(fc_lc);
		if (strcmp(fc_evi->name, fc_versionname) == 0)
			return fc_evi;
	}

	fc_evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo));
	fc_evi->name = pstrdup(fc_versionname);
	fc_evi->reachable = NIL;
	fc_evi->installable = false;
	/* 为稍后应用 Dijkstra 算法进行初始化 */
	fc_evi->distance_known = false;
	fc_evi->distance = INT_MAX;
	fc_evi->previous = NULL;

	*fc_evi_list = lappend(*fc_evi_list, fc_evi);

	return fc_evi;
}

/*
 * 找到最近的未处理的 ExtensionVersionInfo
 *
 * 算法的这一部分同样是 O(N^2)。使用优先队列会使其快得多，但目前没有必要。
 */
static ExtensionVersionInfo * fc_get_nearest_unprocessed_vertex(List *fc_evi_list)
{
	ExtensionVersionInfo *fc_evi = NULL;
	ListCell   *fc_lc;

	foreach(fc_lc, fc_evi_list)
	{
		ExtensionVersionInfo *fc_evi2 = (ExtensionVersionInfo *) lfirst(fc_lc);

		/* 只有距离仍不确定的顶点才是候选 */
		if (fc_evi2->distance_known)
			continue;
		/* 记住最近的这样的顶点 */
		if (fc_evi == NULL ||
			fc_evi->distance > fc_evi2->distance)
			fc_evi = fc_evi2;
	}

	return fc_evi;
}

/*
 * 获取可用于指定扩展的更新脚本集的信息。结果是一个 ExtensionVersionInfo 结构的列表，每个结构都有一个子列表，其中包含可以从该版本一步到达的 ExtensionVersionInfo。
 */
static List * fc_get_ext_ver_list(ExtensionControlFile *fc_control)
{
	List	   *fc_evi_list = NIL;
	int			fc_extnamelen = strlen(fc_control->name);
	char	   *fc_location;
	DIR		   *fc_dir;
	struct dirent *fc_de;

	fc_location = fc_get_extension_script_directory(fc_control);
	fc_dir = AllocateDir(fc_location);
	while ((fc_de = ReadDir(fc_dir, fc_location)) != NULL)
	{
		char	   *fc_vername;
		char	   *fc_vername2;
		ExtensionVersionInfo *fc_evi;
		ExtensionVersionInfo *fc_evi2;

		/* 必须是 .sql 文件 ... */
		if (!fc_is_extension_script_filename(fc_de->d_name))
			continue;

		/* ... 匹配扩展名称后跟分隔符 */
		if (strncmp(fc_de->d_name, fc_control->name, fc_extnamelen) != 0 ||
			fc_de->d_name[fc_extnamelen] != '-' ||
			fc_de->d_name[fc_extnamelen + 1] != '-')
			continue;

		/* 从 'extname--something.sql' 文件名中提取版本名称 */
		fc_vername = pstrdup(fc_de->d_name + fc_extnamelen + 2);
		*strrchr(fc_vername, '.') = '\0';
		fc_vername2 = strstr(fc_vername, "--");
		if (!fc_vername2)
		{
			/* 这是安装而不是更新脚本；记录它的版本名称 */
			fc_evi = fc_get_ext_ver_info(fc_vername, &fc_evi_list);
			fc_evi->installable = true;
			continue;
		}
		*fc_vername2 = '\0';		/* 终止第一个版本 */
		fc_vername2 += 2;			/* 并指向第二个 */

		/* 如果还有第三个--，则为虚假的，忽略它 */
		if (strstr(fc_vername2, "--"))
			continue;

		/* 创建 ExtensionVersionInfos 并将它们链接在一起 */
		fc_evi = fc_get_ext_ver_info(fc_vername, &fc_evi_list);
		fc_evi2 = fc_get_ext_ver_info(fc_vername2, &fc_evi_list);
		fc_evi->reachable = lappend(fc_evi->reachable, fc_evi2);
	}
	FreeDir(fc_dir);

	return fc_evi_list;
}

/*
 * 给定初始版本和最终版本名称，确定需要应用的更新脚本序列以执行该更新。
 *
 * 结果是一个要过渡的版本名称列表（初始版本*不*包括在内）。
 */
static List * fc_identify_update_path(ExtensionControlFile *fc_control,
					 const char *fc_oldVersion, const char *fc_newVersion)
{
	List	   *fc_result;
	List	   *fc_evi_list;
	ExtensionVersionInfo *fc_evi_start;
	ExtensionVersionInfo *fc_evi_target;

	/* 从脚本目录提取版本更新图 */
	fc_evi_list = fc_get_ext_ver_list(fc_control);

	/* 初始化起始和结束顶点 */
	fc_evi_start = fc_get_ext_ver_info(fc_oldVersion, &fc_evi_list);
	fc_evi_target = fc_get_ext_ver_info(fc_newVersion, &fc_evi_list);

	/* 找到最短路径 */
	fc_result = fc_find_update_path(fc_evi_list, fc_evi_start, fc_evi_target, false, false);

	if (fc_result == NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
						fc_control->name, fc_oldVersion, fc_newVersion)));

	return fc_result;
}

/*
 * 应用Dijkstra算法从evi_start找到到evi_target的最短路径。
 *
 * 如果reject_indirect为真，则忽略经过可安装版本的路径。这在调用者考虑从所有可安装版本开始时可以节省工作。
 *
 * 如果reinitialize为假，则假设ExtensionVersionInfo列表之前未被使用，并且get_ext_ver_info完成的初始化仍然有效。否则，重新初始化此处使用的所有瞬态字段。
 *
 * 结果是一个要过渡的版本名称列表（初始版本*不*包括在内）。如果没有这样的路径，返回NIL。
 */
static List * fc_find_update_path(List *fc_evi_list,
				 ExtensionVersionInfo *fc_evi_start,
				 ExtensionVersionInfo *fc_evi_target,
				 bool fc_reject_indirect,
				 bool fc_reinitialize)
{
	List	   *fc_result;
	ExtensionVersionInfo *fc_evi;
	ListCell   *fc_lc;

	/* 如果start == target则调用者错误 */
	Assert(fc_evi_start != fc_evi_target);
	/* 如果reject_indirect且target是可安装的，则调用者错误 */
	Assert(!(fc_reject_indirect && fc_evi_target->installable));

	if (fc_reinitialize)
	{
		foreach(fc_lc, fc_evi_list)
		{
			fc_evi = (ExtensionVersionInfo *) lfirst(fc_lc);
			fc_evi->distance_known = false;
			fc_evi->distance = INT_MAX;
			fc_evi->previous = NULL;
		}
	}

	fc_evi_start->distance = 0;

	while ((fc_evi = fc_get_nearest_unprocessed_vertex(fc_evi_list)) != NULL)
	{
		if (fc_evi->distance == INT_MAX)
			break;				/* 所有剩余顶点都是不可达的 */
		fc_evi->distance_known = true;
		if (fc_evi == fc_evi_target)
			break;				/* 找到到目标的最短路径 */
		foreach(fc_lc, fc_evi->reachable)
		{
			ExtensionVersionInfo *fc_evi2 = (ExtensionVersionInfo *) lfirst(fc_lc);
			int			fc_newdist;

			/* 如果reject_indirect，将可安装版本视为不可达 */
			if (fc_reject_indirect && fc_evi2->installable)
				continue;
			fc_newdist = fc_evi->distance + 1;
			if (fc_newdist < fc_evi2->distance)
			{
				fc_evi2->distance = fc_newdist;
				fc_evi2->previous = fc_evi;
			}
			else if (fc_newdist == fc_evi2->distance &&
					 fc_evi2->previous != NULL &&
					 strcmp(fc_evi->name, fc_evi2->previous->name) < 0)
			{
				/*
				 * 在字符串比较中选择版本名称先到者以打破平局。这种行为没有文档支持，用户不应依赖它。我们这样做只是为了确保如果出现平局，选择的更新路径不会依赖于随机因素，例如目录条目被访问的顺序。
				 */
				fc_evi2->previous = fc_evi;
			}
		}
	}

	/* 如果目标无法从起始点访问，则返回NIL */
	if (!fc_evi_target->distance_known)
		return NIL;

	/* 构建并返回表示更新路径的版本名称列表 */
	fc_result = NIL;
	for (fc_evi = fc_evi_target; fc_evi != fc_evi_start; fc_evi = fc_evi->previous)
		fc_result = lcons(fc_evi->name, fc_result);

	return fc_result;
}

/*
 * 给定一个不可直接安装的目标版本，从一个直接可安装版本开始，找到最佳安装顺序。
 *
 * evi_list：先前收集的版本更新图
 * evi_target：我们想要到达的该列表的成员
 *
 * 返回最佳起始版本，如果没有则返回NULL。成功时，*best_path设置为从起点的路径。
 *
 * 如果有多个可能的起始点，优先选择较短的更新路径，并在起始版本名称的字符串比较基础上任意打破任何平局。
 */
static ExtensionVersionInfo * fc_find_install_path(List *fc_evi_list, ExtensionVersionInfo *fc_evi_target,
				  List **fc_best_path)
{
	ExtensionVersionInfo *fc_evi_start = NULL;
	ListCell   *fc_lc;

	*fc_best_path = NIL;

	/*
	 * 我们不希望在可安装目标上被调用，但如果我们被调用，答案很简单：只需从那里开始，更新路径为空。
	 */
	if (fc_evi_target->installable)
		return fc_evi_target;

	/* 将所有可安装版本视为起始点 */
	foreach(fc_lc, fc_evi_list)
	{
		ExtensionVersionInfo *fc_evi1 = (ExtensionVersionInfo *) lfirst(fc_lc);
		List	   *fc_path;

		if (!fc_evi1->installable)
			continue;

		/*
		 * 找到从evi1到evi_target的最短路径；但不需要考虑经过其他可安装版本的路径。
		 */
		fc_path = fc_find_update_path(fc_evi_list, fc_evi1, fc_evi_target, true, true);
		if (fc_path == NIL)
			continue;

		/* 记住最佳路径 */
		if (fc_evi_start == NULL ||
			list_length(fc_path) < list_length(*fc_best_path) ||
			(list_length(fc_path) == list_length(*fc_best_path) &&
			 strcmp(fc_evi_start->name, fc_evi1->name) < 0))
		{
			fc_evi_start = fc_evi1;
			*fc_best_path = fc_path;
		}
	}

	return fc_evi_start;
}

/*
 * CREATE EXTENSION worker
 *
 * 当指定 CASCADE 时，CreateExtensionInternal() 会递归安装所需的扩展。为了合理处理循环依赖，“父”列表包含已经正在安装的扩展的名称列表，从而允许我们在递归到其中之一时出错。
 */
static ObjectAddress fc_CreateExtensionInternal(char *fc_extensionName,
						char *fc_schemaName,
						const char *fc_versionName,
						bool fc_cascade,
						List *fc_parents,
						bool fc_is_create)
{
	char	   *fc_origSchemaName = fc_schemaName;
	Oid			fc_schemaOid = InvalidOid;
	Oid			fc_extowner = GetUserId();
	ExtensionControlFile *fc_pcontrol;
	ExtensionControlFile *fc_control;
	char	   *fc_filename;
	struct stat fc_fst;
	List	   *fc_updateVersions;
	List	   *fc_requiredExtensions;
	List	   *fc_requiredSchemas;
	Oid			fc_extensionOid;
	ObjectAddress fc_address;
	ListCell   *fc_lc;

	/*
	 * 读取主要控制文件。请注意，我们假设它不包含任何非 ASCII 数据，因此此时无需担心编码问题。
	 */
	fc_pcontrol = fc_read_extension_control_file(fc_extensionName);

	/*
	 * 确定要安装的版本
	 */
	if (fc_versionName == NULL)
	{
		if (fc_pcontrol->default_version)
			fc_versionName = fc_pcontrol->default_version;
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("version to install must be specified")));
	}
	fc_check_valid_version_name(fc_versionName);

	/*
	 * 找出我们需要运行哪个脚本来安装所需版本的扩展。如果没有直接执行所需操作的脚本，我们将尝试找到一系列更新脚本以实现目标。
	 */
	fc_filename = fc_get_extension_script_filename(fc_pcontrol, NULL, fc_versionName);
	if (stat(fc_filename, &fc_fst) == 0)
	{
		/* 简单，无需额外脚本 */
		fc_updateVersions = NIL;
	}
	else
	{
		/* 寻找安装此版本的最佳方法 */
		List	   *fc_evi_list;
		ExtensionVersionInfo *fc_evi_start;
		ExtensionVersionInfo *fc_evi_target;

		/* 从脚本目录提取版本更新图 */
		fc_evi_list = fc_get_ext_ver_list(fc_pcontrol);

		/* 确定目标版本 */
		fc_evi_target = fc_get_ext_ver_info(fc_versionName, &fc_evi_list);

		/* 确定到达目标的最佳路径 */
		fc_evi_start = fc_find_install_path(fc_evi_list, fc_evi_target,
									  &fc_updateVersions);

		/* 如果没有路径则失败... */
		if (fc_evi_start == NULL)
#ifdef FDD //cppcheck
		{
			//cppcheck
#endif
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
							fc_pcontrol->name, fc_versionName)));
#ifdef FDD //cppcheck
			return InvalidObjectAddress;
		}
#endif

		/* 否则，安装最佳起始点，然后升级 */
		fc_versionName = fc_evi_start->name;
	}

	/*
	 * 获取安装目标版本的控制参数
	 */
	fc_control = fc_read_extension_aux_control_file(fc_pcontrol, fc_versionName);

	/*
	 * 确定要安装扩展的目标 schema
	 */
	if (fc_schemaName)
	{
		/* 如果用户提供了 schema 名称，则它必须已经存在。 */
		fc_schemaOid = get_namespace_oid(fc_schemaName, false);
	}

	if (fc_control->schema != NULL)
	{
		/*
		 * 该扩展不可重定位，作者为其提供了 schema。
		 *
		 * 除非指定了 CASCADE 参数，否则如果指定了 control->schema，提供一个与 control->schema 不同的 schema 是错误的。
		 */
		if (fc_schemaName && strcmp(fc_control->schema, fc_schemaName) != 0 &&
			!fc_cascade)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("extension \"%s\" must be installed in schema \"%s\"",
							fc_control->name,
							fc_control->schema)));

		/* 始终使用控制文件中的 schema 作为当前扩展。 */
		fc_schemaName = fc_control->schema;

		/* 查找或创建 schema，以防其不存在。 */
		fc_schemaOid = get_namespace_oid(fc_schemaName, true);

		if (!OidIsValid(fc_schemaOid))
		{
			CreateSchemaStmt *fc_csstmt = makeNode(CreateSchemaStmt);

			fc_csstmt->schemaname = fc_schemaName;
			fc_csstmt->authrole = NULL;	/* 将由当前用户创建 */
			fc_csstmt->schemaElts = NIL;
			fc_csstmt->if_not_exists = false;
			CreateSchemaCommand(fc_csstmt, "(generated CREATE SCHEMA command)",
								-1, -1);

			/*
			 * CreateSchemaCommand 包括 CommandCounterIncrement，因此新
			 * schema 现在是可见的。
			 */
			fc_schemaOid = get_namespace_oid(fc_schemaName, false);
		}
	}
	else if (!OidIsValid(fc_schemaOid))
	{
		/*
		 * 既没有用户也没有扩展的作者指定 schema；使用当前默认创建命名空间，即 search_path 中的第一个显式条目。
		 */
		List	   *fc_search_path = fetch_search_path(false);

		if (fc_search_path == NIL) /* search_path 中没有有效项？ */
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_SCHEMA),
					 errmsg("no schema has been selected to create in")));
		fc_schemaOid = linitial_oid(fc_search_path);
		fc_schemaName = get_namespace_name(fc_schemaOid);
		if (fc_schemaName == NULL) /* 最近删除的命名空间？ */
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_SCHEMA),
					 errmsg("no schema has been selected to create in")));

		list_free(fc_search_path);
	}

	/*
	 * 注意，在此事务中是否访问了临时命名空间。
	 */
	if (isTempNamespace(fc_schemaOid))
		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

	/*
	 * 我们在这里不检查目标命名空间的创建权限。如果扩展脚本实际上在那里创建任何对象，如果用户没有这样的权限，将会失败。但在一些情况下，例如过程语言，方便将 schema 设置为 pg_catalog，但我们不想将命令限制为具有 pg_catalog 的 ACL_CREATE 权限的用户。
	 */

	/*
	 * 查找必需的扩展，必要时安装它们，并构建它们的 OID 和目标 schema 的 OID 列表。
	 */
	fc_requiredExtensions = NIL;
	fc_requiredSchemas = NIL;
	foreach(fc_lc, fc_control->requires)
	{
		char	   *fc_curreq = (char *) lfirst(fc_lc);
		Oid			fc_reqext;
		Oid			fc_reqschema;

		fc_reqext = fc_get_required_extension(fc_curreq,
										fc_extensionName,
										fc_origSchemaName,
										fc_cascade,
										fc_parents,
										fc_is_create);
		fc_reqschema = fc_get_extension_schema(fc_reqext);
		fc_requiredExtensions = lappend_oid(fc_requiredExtensions, fc_reqext);
		fc_requiredSchemas = lappend_oid(fc_requiredSchemas, fc_reqschema);
	}

	/*
	 * 在 pg_extension 中插入新的元组，并创建依赖项条目。
	 */
	fc_address = InsertExtensionTuple(fc_control->name, fc_extowner,
								   fc_schemaOid, fc_control->relocatable,
								   fc_versionName,
								   PointerGetDatum(NULL),
								   PointerGetDatum(NULL),
								   fc_requiredExtensions);
	fc_extensionOid = fc_address.objectId;

	/*
	 * 应用扩展的任何控制文件注释
	 */
	if (fc_control->comment != NULL)
		CreateComments(fc_extensionOid, ExtensionRelationId, 0, fc_control->comment);

	/*
	 * 执行安装脚本文件
	 */
	fc_execute_extension_script(fc_extensionOid, fc_control,
							 NULL, fc_versionName,
							 fc_requiredSchemas,
							 fc_schemaName, fc_schemaOid);

	/*
	 * 如果需要执行额外的更新脚本，将这些更新视为一系列 ALTER EXTENSION UPDATE 命令
	 */
	fc_ApplyExtensionUpdates(fc_extensionOid, fc_pcontrol,
						  fc_versionName, fc_updateVersions,
						  fc_origSchemaName, fc_cascade, fc_is_create);

	return fc_address;
}

/*
 * 获取 "requires" 中列出的扩展的 OID，可能会创建它。
 */
static Oid fc_get_required_extension(char *fc_reqExtensionName,
					   char *fc_extensionName,
					   char *fc_origSchemaName,
					   bool fc_cascade,
					   List *fc_parents,
					   bool fc_is_create)
{
	Oid			fc_reqExtensionOid;

	fc_reqExtensionOid = get_extension_oid(fc_reqExtensionName, true);
	if (!OidIsValid(fc_reqExtensionOid))
	{
		if (fc_cascade)
		{
			/* 必须安装它。 */
			ObjectAddress fc_addr;
			List	   *fc_cascade_parents;
			ListCell   *fc_lc;

			/* 在尝试级联之前检查扩展名称的有效性。 */
			fc_check_valid_extension_name(fc_reqExtensionName);

			/* 检查扩展之间的循环依赖。 */
			foreach(fc_lc, fc_parents)
			{
				char	   *fc_pname = (char *) lfirst(fc_lc);

				if (strcmp(fc_pname, fc_reqExtensionName) == 0)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_RECURSION),
							 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
									fc_reqExtensionName, fc_extensionName)));
			}

			ereport(NOTICE,
					(errmsg("installing required extension \"%s\"",
							fc_reqExtensionName)));

			/* 将当前扩展添加到要传递下去的父项列表中。 */
			fc_cascade_parents = lappend(list_copy(fc_parents), fc_extensionName);

			/*
			 * 创建所需的扩展。 如果有，传播 SCHEMA 选项和 CASCADE，但不传播其他选项。
			 */
			fc_addr = fc_CreateExtensionInternal(fc_reqExtensionName,
										   fc_origSchemaName,
										   NULL,
										   fc_cascade,
										   fc_cascade_parents,
										   fc_is_create);

			/* 获取其新分配的 OID。 */
			fc_reqExtensionOid = fc_addr.objectId;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("required extension \"%s\" is not installed",
							fc_reqExtensionName),
					 fc_is_create ?
					 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
	}

	return fc_reqExtensionOid;
}

/*
 * CREATE EXTENSION
 */
ObjectAddress CreateExtension(ParseState *fc_pstate, CreateExtensionStmt *fc_stmt)
{
	DefElem    *fc_d_schema = NULL;
	DefElem    *fc_d_new_version = NULL;
	DefElem    *fc_d_cascade = NULL;
	char	   *fc_schemaName = NULL;
	char	   *fc_versionName = NULL;
	bool		fc_cascade = false;
	ListCell   *fc_lc;

	/* 在任何文件系统访问之前检查扩展名称的有效性 */
	fc_check_valid_extension_name(fc_stmt->extname);

	/*
	 * 检查重复的扩展名称。 pg_extension.extname 上的唯一索引会捕获此问题，并作为在竞争条件下的后备；但这是一个更友好的错误信息，并且我们需要检查以支持 IF NOT EXISTS。
	 */
	if (get_extension_oid(fc_stmt->extname, true) != InvalidOid)
	{
		if (fc_stmt->if_not_exists)
		{
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("extension \"%s\" already exists, skipping",
							fc_stmt->extname)));
			return InvalidObjectAddress;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("extension \"%s\" already exists",
							fc_stmt->extname)));
	}

	/*
	 * 我们使用全局变量来跟踪正在创建的扩展，以便我们一次只能创建一个扩展。
	 */
	if (creating_extension)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("nested CREATE EXTENSION is not supported")));

	/* 解构语句选项列表 */
	foreach(fc_lc, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_lc);

		if (strcmp(fc_defel->defname, "schema") == 0)
		{
			if (fc_d_schema)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_d_schema = fc_defel;
			fc_schemaName = defGetString(fc_d_schema);
		}
		else if (strcmp(fc_defel->defname, "new_version") == 0)
		{
			if (fc_d_new_version)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_d_new_version = fc_defel;
			fc_versionName = defGetString(fc_d_new_version);
		}
		else if (strcmp(fc_defel->defname, "cascade") == 0)
		{
			if (fc_d_cascade)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_d_cascade = fc_defel;
			fc_cascade = defGetBoolean(fc_d_cascade);
		}
		else
			elog(ERROR, "unrecognized option: %s", fc_defel->defname);
	}

	/* 调用 CreateExtensionInternal 执行实际工作。 */
	return fc_CreateExtensionInternal(fc_stmt->extname,
								   fc_schemaName,
								   fc_versionName,
								   fc_cascade,
								   NIL,
								   true);
}

/*
 * InsertExtensionTuple
 *
 * 插入新的 pg_extension 行，并创建扩展的依赖条目。
 * 返回分配给新行的 OID。
 *
 * 这个接口是为了 pg_upgrade 的利益而导出的，因为它必须在不实际运行扩展脚本的情况下创建一个 pg_extension 条目（及扩展级依赖）。
 *
 * extConfig 和 extCondition 应该是数组或 PointerGetDatum(NULL)。
 * 我们将它们声明为普通 Datum，以避免在 extension.h 中需要 array.h。
 */
ObjectAddress InsertExtensionTuple(const char *fc_extName, Oid fc_extOwner,
					 Oid fc_schemaOid, bool fc_relocatable, const char *fc_extVersion,
					 Datum fc_extConfig, Datum fc_extCondition,
					 List *fc_requiredExtensions)
{
	Oid			fc_extensionOid;
	Relation	fc_rel;
	Datum		fc_values[Natts_pg_extension];
	bool		fc_nulls[Natts_pg_extension];
	HeapTuple	fc_tuple;
	ObjectAddress fc_myself;
	ObjectAddress fc_nsp;
	ObjectAddresses *fc_refobjs;
	ListCell   *fc_lc;

	/*
	 * 构建并插入 pg_extension 元组
	 */
	fc_rel = table_open(ExtensionRelationId, RowExclusiveLock);

	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_nulls, 0, sizeof(fc_nulls));

	fc_extensionOid = GetNewOidWithIndex(fc_rel, ExtensionOidIndexId,
									  Anum_pg_extension_oid);
	fc_values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(fc_extensionOid);
	fc_values[Anum_pg_extension_extname - 1] =
		DirectFunctionCall1(namein, CStringGetDatum(fc_extName));
	fc_values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(fc_extOwner);
	fc_values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(fc_schemaOid);
	fc_values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(fc_relocatable);
	fc_values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(fc_extVersion);

	if (fc_extConfig == PointerGetDatum(NULL))
		fc_nulls[Anum_pg_extension_extconfig - 1] = true;
	else
		fc_values[Anum_pg_extension_extconfig - 1] = fc_extConfig;

	if (fc_extCondition == PointerGetDatum(NULL))
		fc_nulls[Anum_pg_extension_extcondition - 1] = true;
	else
		fc_values[Anum_pg_extension_extcondition - 1] = fc_extCondition;

	fc_tuple = heap_form_tuple(fc_rel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_rel, fc_tuple);

	heap_freetuple(fc_tuple);
	table_close(fc_rel, RowExclusiveLock);

	/*
	 * 记录对所有者、模式和前提扩展的依赖关系
	 */
	recordDependencyOnOwner(ExtensionRelationId, fc_extensionOid, fc_extOwner);

	fc_refobjs = new_object_addresses();

	ObjectAddressSet(fc_myself, ExtensionRelationId, fc_extensionOid);

	ObjectAddressSet(fc_nsp, NamespaceRelationId, fc_schemaOid);
	add_exact_object_address(&fc_nsp, fc_refobjs);

	foreach(fc_lc, fc_requiredExtensions)
	{
		Oid			fc_reqext = lfirst_oid(fc_lc);
		ObjectAddress fc_otherext;

		ObjectAddressSet(fc_otherext, ExtensionRelationId, fc_reqext);
		add_exact_object_address(&fc_otherext, fc_refobjs);
	}

	/* 记录所有这些（包括重复消除） */
	record_object_address_dependencies(&fc_myself, fc_refobjs, DEPENDENCY_NORMAL);
	free_object_addresses(fc_refobjs);

	/* 新扩展的创建后钩子 */
	InvokeObjectPostCreateHook(ExtensionRelationId, fc_extensionOid, 0);

	return fc_myself;
}

/*
 * 扩展删除的核心。
 *
 * 我们在这里需要做的就是删除 pg_extension 元组本身。 其他一切都由依赖基础设施处理。
 */
void RemoveExtensionById(Oid fc_extId)
{
	Relation	fc_rel;
	SysScanDesc fc_scandesc;
	HeapTuple	fc_tuple;
	ScanKeyData fc_entry[1];

	/*
	 * 禁止删除任何当前正处于插入状态的扩展； 
	 * 否则在 subsequent 运行 recordDependencyOnCurrentExtension() 时
	 * 可能会创建悬空的 pg_depend 记录，指向一个不再有效的 pg_extension OID。 
	 * 这一点并不是因为我们认为人们可能在 foo 的自己脚本文件中写“DROP EXTENSION foo”，
	 * 而是因为扩展脚本文件中依赖管理的错误可能导致某个包含对象递归的结果，进而删除一个扩展。
	 * 因此我们必须在这里测试此情况，而不是在 DROP EXTENSION 命令的更高层次上。
	 */
	if (fc_extId == CurrentExtensionObject)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("cannot drop extension \"%s\" because it is being modified",
						get_extension_name(fc_extId))));

	fc_rel = table_open(ExtensionRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_extId));
	fc_scandesc = systable_beginscan(fc_rel, ExtensionOidIndexId, true,
								  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scandesc);

	/* 我们假设最多只有一个匹配的元组 */
	if (HeapTupleIsValid(fc_tuple))
		CatalogTupleDelete(fc_rel, &fc_tuple->t_self);

	systable_endscan(fc_scandesc);

	table_close(fc_rel, RowExclusiveLock);
}

/*
 * 此函数列出可用的扩展（控制目录中每个主控制文件一行）。我们解析每个控制文件并报告有趣的字段。
 *
 * 系统视图 pg_available_extensions 提供了一个用户界面，添加有关扩展是否已安装在当前数据库中的信息。
 */
Datum pg_available_extensions(PG_FUNCTION_ARGS)
{
	ReturnSetInfo *fc_rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	char	   *fc_location;
	DIR		   *fc_dir;
	struct dirent *fc_de;

	/* 构建元组存储以保存结果行 */
	InitMaterializedSRF(fcinfo, 0);

	fc_location = fc_get_extension_control_directory();
	fc_dir = AllocateDir(fc_location);

	/*
	 * 如果控制目录不存在，我们希望静默返回一个空集合。任何其他错误将由 ReadDir 报告。
	 */
	if (fc_dir == NULL && errno == ENOENT)
	{
		/* 不执行任何操作 */
	}
	else
	{
		while ((fc_de = ReadDir(fc_dir, fc_location)) != NULL)
		{
			ExtensionControlFile *fc_control;
			char	   *fc_extname;
			Datum		fc_values[3];
			bool		fc_nulls[3];

			if (!fc_is_extension_control_filename(fc_de->d_name))
				continue;

			/* 从 'name.control' 文件名中提取扩展名 */
			fc_extname = pstrdup(fc_de->d_name);
			*strrchr(fc_extname, '.') = '\0';

			/* 如果是辅助控制文件则忽略 */
			if (strstr(fc_extname, "--"))
				continue;

			fc_control = fc_read_extension_control_file(fc_extname);

			memset(fc_values, 0, sizeof(fc_values));
			memset(fc_nulls, 0, sizeof(fc_nulls));

			/* 名称 */
			fc_values[0] = DirectFunctionCall1(namein,
											CStringGetDatum(fc_control->name));
			/* 默认版本 */
			if (fc_control->default_version == NULL)
				fc_nulls[1] = true;
			else
				fc_values[1] = CStringGetTextDatum(fc_control->default_version);
			/* 评论 */
			if (fc_control->comment == NULL)
				fc_nulls[2] = true;
			else
				fc_values[2] = CStringGetTextDatum(fc_control->comment);

			tuplestore_putvalues(fc_rsinfo->setResult, fc_rsinfo->setDesc,
								 fc_values, fc_nulls);
		}

		FreeDir(fc_dir);
	}

	return (Datum) 0;
}

/*
 * 此函数列出可用的扩展版本（每个扩展安装脚本一行）。对于每个版本，我们解析相关的控制文件，并报告有趣的字段。
 *
 * 系统视图 pg_available_extension_versions 提供了一个用户界面，添加有关当前数据库中安装了哪些版本的信息。
 */
Datum pg_available_extension_versions(PG_FUNCTION_ARGS)
{
	ReturnSetInfo *fc_rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	char	   *fc_location;
	DIR		   *fc_dir;
	struct dirent *fc_de;

	/* 构建元组存储以保存结果行 */
	InitMaterializedSRF(fcinfo, 0);

	fc_location = fc_get_extension_control_directory();
	fc_dir = AllocateDir(fc_location);

	/*
	 * 如果控制目录不存在，我们希望静默返回一个空集合。任何其他错误将由 ReadDir 报告。
	 */
	if (fc_dir == NULL && errno == ENOENT)
	{
		/* 不执行任何操作 */
	}
	else
	{
		while ((fc_de = ReadDir(fc_dir, fc_location)) != NULL)
		{
			ExtensionControlFile *fc_control;
			char	   *fc_extname;

			if (!fc_is_extension_control_filename(fc_de->d_name))
				continue;

			/* 从 'name.control' 文件名中提取扩展名 */
			fc_extname = pstrdup(fc_de->d_name);
			*strrchr(fc_extname, '.') = '\0';

			/* 如果是辅助控制文件则忽略 */
			if (strstr(fc_extname, "--"))
				continue;

			/* 读取控制文件 */
			fc_control = fc_read_extension_control_file(fc_extname);

			/* 扫描扩展的脚本目录以查找安装脚本 */
			fc_get_available_versions_for_extension(fc_control, fc_rsinfo->setResult,
												 fc_rsinfo->setDesc);
		}

		FreeDir(fc_dir);
	}

	return (Datum) 0;
}

/*
 * pg_available_extension_versions 的内层循环：
 *		读取一个扩展的版本，将行添加到元组存储
 */
static void fc_get_available_versions_for_extension(ExtensionControlFile *fc_pcontrol,
									 Tuplestorestate *fc_tupstore,
									 TupleDesc fc_tupdesc)
{
	List	   *fc_evi_list;
	ListCell   *fc_lc;

	/* 从脚本目录提取版本更新图 */
	fc_evi_list = fc_get_ext_ver_list(fc_pcontrol);

	/* 对于每个可安装版本... */
	foreach(fc_lc, fc_evi_list)
	{
		ExtensionVersionInfo *fc_evi = (ExtensionVersionInfo *) lfirst(fc_lc);
		ExtensionControlFile *fc_control;
		Datum		fc_values[8];
		bool		fc_nulls[8];
		ListCell   *fc_lc2;

		if (!fc_evi->installable)
			continue;

		/*
		 * 获取特定版本的参数（pcontrol 不变）
		 */
		fc_control = fc_read_extension_aux_control_file(fc_pcontrol, fc_evi->name);

		memset(fc_values, 0, sizeof(fc_values));
		memset(fc_nulls, 0, sizeof(fc_nulls));

		/* 名称 */
		fc_values[0] = DirectFunctionCall1(namein,
										CStringGetDatum(fc_control->name));
		/* 版本 */
		fc_values[1] = CStringGetTextDatum(fc_evi->name);
		/* 超级用户 */
		fc_values[2] = BoolGetDatum(fc_control->superuser);
		/* 可信 */
		fc_values[3] = BoolGetDatum(fc_control->trusted);
		/* 可重定位 */
		fc_values[4] = BoolGetDatum(fc_control->relocatable);
		/* schema */
		if (fc_control->schema == NULL)
			fc_nulls[5] = true;
		else
			fc_values[5] = DirectFunctionCall1(namein,
											CStringGetDatum(fc_control->schema));
		/* 需要 */
		if (fc_control->requires == NIL)
			fc_nulls[6] = true;
		else
			fc_values[6] = fc_convert_requires_to_datum(fc_control->requires);
		/* 评论 */
		if (fc_control->comment == NULL)
			fc_nulls[7] = true;
		else
			fc_values[7] = CStringGetTextDatum(fc_control->comment);

		tuplestore_putvalues(fc_tupstore, fc_tupdesc, fc_values, fc_nulls);

		/*
		 * 查找所有无法直接安装的版本，这些版本将从此版本开始安装，并报告它们，继承在此版本更新中未更改的参数。
		 */
		foreach(fc_lc2, fc_evi_list)
		{
			ExtensionVersionInfo *fc_evi2 = (ExtensionVersionInfo *) lfirst(fc_lc2);
			List	   *fc_best_path;

			if (fc_evi2->installable)
				continue;
			if (fc_find_install_path(fc_evi_list, fc_evi2, &fc_best_path) == fc_evi)
			{
				/*
				 * 获取此版本的参数（pcontrol 不变）
				 */
				fc_control = fc_read_extension_aux_control_file(fc_pcontrol, fc_evi2->name);

				/* 名称保持不变 */
				/* 版本 */
				fc_values[1] = CStringGetTextDatum(fc_evi2->name);
				/* 超级用户 */
				fc_values[2] = BoolGetDatum(fc_control->superuser);
				/* 可信 */
				fc_values[3] = BoolGetDatum(fc_control->trusted);
				/* 可重定位 */
				fc_values[4] = BoolGetDatum(fc_control->relocatable);
				/* 模式保持不变 */
				/* 需要 */
				if (fc_control->requires == NIL)
					fc_nulls[6] = true;
				else
				{
					fc_values[6] = fc_convert_requires_to_datum(fc_control->requires);
					fc_nulls[6] = false;
				}
				/* 评论保持不变 */

				tuplestore_putvalues(fc_tupstore, fc_tupdesc, fc_values, fc_nulls);
			}
		}
	}
}

/*
 * 测试给定扩展是否存在（而不是是否已安装）
 *
 * 这检查扩展目录中是否存在匹配的控制文件。这并不是一个万无一失的检查，因为文件可能无效，但这仅用于提示，因此它不必 100% 正确。
 */
bool extension_file_exists(const char *fc_extensionName)
{
	bool		fc_result = false;
	char	   *fc_location;
	DIR		   *fc_dir;
	struct dirent *fc_de;

	fc_location = fc_get_extension_control_directory();
	fc_dir = AllocateDir(fc_location);

	/*
	 * 如果控制目录不存在，我们希望静默返回
	 * false。任何其他错误将由 ReadDir 报告。
	 */
	if (fc_dir == NULL && errno == ENOENT)
	{
		/* 不执行任何操作 */
	}
	else
	{
		while ((fc_de = ReadDir(fc_dir, fc_location)) != NULL)
		{
			char	   *fc_extname;

			if (!fc_is_extension_control_filename(fc_de->d_name))
				continue;

			/* 从 'name.control' 文件名中提取扩展名 */
			fc_extname = pstrdup(fc_de->d_name);
			*strrchr(fc_extname, '.') = '\0';

			/* 如果是辅助控制文件则忽略 */
			if (strstr(fc_extname, "--"))
				continue;

			/* 如果它匹配请求则完成 */
			if (strcmp(fc_extname, fc_extensionName) == 0)
			{
				fc_result = true;
				break;
			}
		}

		FreeDir(fc_dir);
	}

	return fc_result;
}

/*
 * 将扩展名列表转换为 name[] Datum
 */
static Datum fc_convert_requires_to_datum(List *fc_requires)
{
	Datum	   *fc_datums;
	int			fc_ndatums;
	ArrayType  *fc_a;
	ListCell   *fc_lc;

	fc_ndatums = list_length(fc_requires);
	fc_datums = (Datum *) palloc(fc_ndatums * sizeof(Datum));
	fc_ndatums = 0;
	foreach(fc_lc, fc_requires)
	{
		char	   *fc_curreq = (char *) lfirst(fc_lc);

		fc_datums[fc_ndatums++] =
			DirectFunctionCall1(namein, CStringGetDatum(fc_curreq));
	}
	fc_a = construct_array(fc_datums, fc_ndatums,
						NAMEOID,
						NAMEDATALEN, false, TYPALIGN_CHAR);
	return PointerGetDatum(fc_a);
}

/*
 * 此函数报告指定扩展的版本更新路径。
 */
Datum pg_extension_update_paths(PG_FUNCTION_ARGS)
{
	Name		fc_extname = PG_GETARG_NAME(0);
	ReturnSetInfo *fc_rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	List	   *fc_evi_list;
	ExtensionControlFile *fc_control;
	ListCell   *fc_lc1;

	/* 在任何文件系统访问之前检查扩展名称的有效性 */
	fc_check_valid_extension_name(NameStr(*fc_extname));

	/* 构建元组存储以保存结果行 */
	InitMaterializedSRF(fcinfo, 0);

	/* 读取扩展的控制文件 */
	fc_control = fc_read_extension_control_file(NameStr(*fc_extname));

	/* 从脚本目录提取版本更新图 */
	fc_evi_list = fc_get_ext_ver_list(fc_control);

	/* 遍历所有版本对 */
	foreach(fc_lc1, fc_evi_list)
	{
		ExtensionVersionInfo *fc_evi1 = (ExtensionVersionInfo *) lfirst(fc_lc1);
		ListCell   *fc_lc2;

		foreach(fc_lc2, fc_evi_list)
		{
			ExtensionVersionInfo *fc_evi2 = (ExtensionVersionInfo *) lfirst(fc_lc2);
			List	   *fc_path;
			Datum		fc_values[3];
			bool		fc_nulls[3];

			if (fc_evi1 == fc_evi2)
				continue;

			/* 找到从 evi1 到 evi2 的最短路径 */
			fc_path = fc_find_update_path(fc_evi_list, fc_evi1, fc_evi2, false, true);

			/* 发出结果行 */
			memset(fc_values, 0, sizeof(fc_values));
			memset(fc_nulls, 0, sizeof(fc_nulls));

			/* 源 */
			fc_values[0] = CStringGetTextDatum(fc_evi1->name);
			/* 目标 */
			fc_values[1] = CStringGetTextDatum(fc_evi2->name);
			/* 路径 */
			if (fc_path == NIL)
				fc_nulls[2] = true;
			else
			{
				StringInfoData fc_pathbuf;
				ListCell   *fc_lcv;

				initStringInfo(&fc_pathbuf);
				/* 路径不包括起始顶点，但显示它 */
				appendStringInfoString(&fc_pathbuf, fc_evi1->name);
				foreach(fc_lcv, fc_path)
				{
					char	   *fc_versionName = (char *) lfirst(fc_lcv);

					appendStringInfoString(&fc_pathbuf, "--");
					appendStringInfoString(&fc_pathbuf, fc_versionName);
				}
				fc_values[2] = CStringGetTextDatum(fc_pathbuf.data);
				pfree(fc_pathbuf.data);
			}

			tuplestore_putvalues(fc_rsinfo->setResult, fc_rsinfo->setDesc,
								 fc_values, fc_nulls);
		}
	}

	return (Datum) 0;
}

/*
 * pg_extension_config_dump
 *
 * 记录关于即将创建的扩展的配置表的信息，但其内容应该在
 * pg_dump 期间完全或部分转储。
 */
Datum pg_extension_config_dump(PG_FUNCTION_ARGS)
{
	Oid			fc_tableoid = PG_GETARG_OID(0);
	text	   *fc_wherecond = PG_GETARG_TEXT_PP(1);
	char	   *fc_tablename;
	Relation	fc_extRel;
	ScanKeyData fc_key[1];
	SysScanDesc fc_extScan;
	HeapTuple	fc_extTup;
	Datum		fc_arrayDatum;
	Datum		fc_elementDatum;
	int			fc_arrayLength;
	int			fc_arrayIndex;
	bool		fc_isnull;
	Datum		fc_repl_val[Natts_pg_extension];
	bool		fc_repl_null[Natts_pg_extension];
	bool		fc_repl_repl[Natts_pg_extension];
	ArrayType  *fc_a;

	/*
	 * 我们只允许从扩展的 SQL 脚本中调用此函数。我们
	 * 不应该需要超出此之外的任何权限检查。
	 */
	if (!creating_extension)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
						"pg_extension_config_dump()")));

	/*
	 * 检查表是否存在，并且是正在创建的扩展的成员。
	 * 这确保我们不需要注册额外的依赖关系来保护 extconfig 条目。
	 */
	fc_tablename = get_rel_name(fc_tableoid);
	if (fc_tablename == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg("OID %u does not refer to a table", fc_tableoid)));
	if (getExtensionOfObject(RelationRelationId, fc_tableoid) !=
		CurrentExtensionObject)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("table \"%s\" is not a member of the extension being created",
						fc_tablename)));

	/*
	 * 将表的 OID 和 WHERE 条件添加到扩展的 extconfig 和
	 * extcondition 数组中。
	 *
	 * 如果表已经在 extconfig 中，将其视为 WHERE 条件的更新。
	 */

	/* 找到 pg_extension 元组 */
	fc_extRel = table_open(ExtensionRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(CurrentExtensionObject));

	fc_extScan = systable_beginscan(fc_extRel, ExtensionOidIndexId, true,
								 NULL, 1, fc_key);

	fc_extTup = systable_getnext(fc_extScan);

	if (!HeapTupleIsValid(fc_extTup))	/* 不应该发生 */
		elog(ERROR, "could not find tuple for extension %u",
			 CurrentExtensionObject);

	memset(fc_repl_val, 0, sizeof(fc_repl_val));
	memset(fc_repl_null, false, sizeof(fc_repl_null));
	memset(fc_repl_repl, false, sizeof(fc_repl_repl));

	/* 构建或修改 extconfig 值 */
	fc_elementDatum = ObjectIdGetDatum(fc_tableoid);

	fc_arrayDatum = heap_getattr(fc_extTup, Anum_pg_extension_extconfig,
							  RelationGetDescr(fc_extRel), &fc_isnull);
	if (fc_isnull)
	{
		/* 先前为空的 extconfig，因此构建 1 元素数组 */
		fc_arrayLength = 0;
		fc_arrayIndex = 1;

		fc_a = construct_array(&fc_elementDatum, 1,
							OIDOID,
							sizeof(Oid), true, TYPALIGN_INT);
	}
	else
	{
		/* 修改或扩展现有的 extconfig 数组 */
		Oid		   *fc_arrayData;
		int			fc_i;

		fc_a = DatumGetArrayTypeP(fc_arrayDatum);

		fc_arrayLength = ARR_DIMS(fc_a)[0];
		if (ARR_NDIM(fc_a) != 1 ||
			ARR_LBOUND(fc_a)[0] != 1 ||
			fc_arrayLength < 0 ||
			ARR_HASNULL(fc_a) ||
			ARR_ELEMTYPE(fc_a) != OIDOID)
			elog(ERROR, "extconfig is not a 1-D Oid array");
		fc_arrayData = (Oid *) ARR_DATA_PTR(fc_a);

		fc_arrayIndex = fc_arrayLength + 1;	/* 设置在末尾添加 */

		for (fc_i = 0; fc_i < fc_arrayLength; fc_i++)
		{
			if (fc_arrayData[fc_i] == fc_tableoid)
			{
				fc_arrayIndex = fc_i + 1; /* 替换此元素 */
				break;
			}
		}

		fc_a = array_set(fc_a, 1, &fc_arrayIndex,
					  fc_elementDatum,
					  false,
					  -1 /* varlena 数组 */ ,
					  sizeof(Oid) /* OID 的 typlen */ ,
					  true /* OID 的 typbyval */ ,
					  TYPALIGN_INT /* OID 的 typalign */ );
	}
	fc_repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(fc_a);
	fc_repl_repl[Anum_pg_extension_extconfig - 1] = true;

	/* 构建或修改 extcondition 值 */
	fc_elementDatum = PointerGetDatum(fc_wherecond);

	fc_arrayDatum = heap_getattr(fc_extTup, Anum_pg_extension_extcondition,
							  RelationGetDescr(fc_extRel), &fc_isnull);
	if (fc_isnull)
	{
		if (fc_arrayLength != 0)
			elog(ERROR, "extconfig and extcondition arrays do not match");

		fc_a = construct_array(&fc_elementDatum, 1,
							TEXTOID,
							-1, false, TYPALIGN_INT);
	}
	else
	{
		fc_a = DatumGetArrayTypeP(fc_arrayDatum);

		if (ARR_NDIM(fc_a) != 1 ||
			ARR_LBOUND(fc_a)[0] != 1 ||
			ARR_HASNULL(fc_a) ||
			ARR_ELEMTYPE(fc_a) != TEXTOID)
			elog(ERROR, "extcondition is not a 1-D text array");
		if (ARR_DIMS(fc_a)[0] != fc_arrayLength)
			elog(ERROR, "extconfig and extcondition arrays do not match");

		/* 在与 extconfig 相同的索引处添加或替换 */
		fc_a = array_set(fc_a, 1, &fc_arrayIndex,
					  fc_elementDatum,
					  false,
					  -1 /* varlena 数组 */ ,
					  -1 /* TEXT 的 typlen */ ,
					  false /* TEXT 的 typbyval */ ,
					  TYPALIGN_INT /* TEXT 的 typalign */ );
	}
	fc_repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(fc_a);
	fc_repl_repl[Anum_pg_extension_extcondition - 1] = true;

	fc_extTup = heap_modify_tuple(fc_extTup, RelationGetDescr(fc_extRel),
							   fc_repl_val, fc_repl_null, fc_repl_repl);

	CatalogTupleUpdate(fc_extRel, &fc_extTup->t_self, fc_extTup);

	systable_endscan(fc_extScan);

	table_close(fc_extRel, RowExclusiveLock);

	PG_RETURN_VOID();
}

/*
 * extension_config_remove
 *
 * 从扩展的 extconfig 中移除指定的表 OID（如果存在）。
 * 目前这并未作为函数暴露，但可以；
 * 现在，我们只是从 ALTER EXTENSION DROP 调用它。
 */
static void fc_extension_config_remove(Oid fc_extensionoid, Oid fc_tableoid)
{
	Relation	fc_extRel;
	ScanKeyData fc_key[1];
	SysScanDesc fc_extScan;
	HeapTuple	fc_extTup;
	Datum		fc_arrayDatum;
	int			fc_arrayLength;
	int			fc_arrayIndex;
	bool		fc_isnull;
	Datum		fc_repl_val[Natts_pg_extension];
	bool		fc_repl_null[Natts_pg_extension];
	bool		fc_repl_repl[Natts_pg_extension];
	ArrayType  *fc_a;

	/* 找到 pg_extension 元组 */
	fc_extRel = table_open(ExtensionRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_extensionoid));

	fc_extScan = systable_beginscan(fc_extRel, ExtensionOidIndexId, true,
								 NULL, 1, fc_key);

	fc_extTup = systable_getnext(fc_extScan);

	if (!HeapTupleIsValid(fc_extTup))	/* 不应该发生 */
		elog(ERROR, "could not find tuple for extension %u",
			 fc_extensionoid);

	/* 在 extconfig 中搜索 tableoid */
	fc_arrayDatum = heap_getattr(fc_extTup, Anum_pg_extension_extconfig,
							  RelationGetDescr(fc_extRel), &fc_isnull);
	if (fc_isnull)
	{
		/* 无需操作 */
		fc_a = NULL;
		fc_arrayLength = 0;
		fc_arrayIndex = -1;
	}
	else
	{
		Oid		   *fc_arrayData;
		int			fc_i;

		fc_a = DatumGetArrayTypeP(fc_arrayDatum);

		fc_arrayLength = ARR_DIMS(fc_a)[0];
		if (ARR_NDIM(fc_a) != 1 ||
			ARR_LBOUND(fc_a)[0] != 1 ||
			fc_arrayLength < 0 ||
			ARR_HASNULL(fc_a) ||
			ARR_ELEMTYPE(fc_a) != OIDOID)
			elog(ERROR, "extconfig is not a 1-D Oid array");
		fc_arrayData = (Oid *) ARR_DATA_PTR(fc_a);

		fc_arrayIndex = -1;		/* 无需删除的标志 */

		for (fc_i = 0; fc_i < fc_arrayLength; fc_i++)
		{
			if (fc_arrayData[fc_i] == fc_tableoid)
			{
				fc_arrayIndex = fc_i; /* 要移除的索引 */
				break;
			}
		}
	}

	/* 如果 tableoid 不在 extconfig 中，无事可做 */
	if (fc_arrayIndex < 0)
	{
		systable_endscan(fc_extScan);
		table_close(fc_extRel, RowExclusiveLock);
		return;
	}

	/* 修改或删除 extconfig 值 */
	memset(fc_repl_val, 0, sizeof(fc_repl_val));
	memset(fc_repl_null, false, sizeof(fc_repl_null));
	memset(fc_repl_repl, false, sizeof(fc_repl_repl));

	if (fc_arrayLength <= 1)
	{
		/* 仅移除元素，仅将数组设置为 null */
		fc_repl_null[Anum_pg_extension_extconfig - 1] = true;
	}
	else
	{
		/* 压缩目标元素 */
		Datum	   *fc_dvalues;
		int			fc_nelems;
		int			fc_i;

		/* 我们已经检查过没有空值 */
		deconstruct_array(fc_a, OIDOID, sizeof(Oid), true, TYPALIGN_INT,
						  &fc_dvalues, NULL, &fc_nelems);

		for (fc_i = fc_arrayIndex; fc_i < fc_arrayLength - 1; fc_i++)
			fc_dvalues[fc_i] = fc_dvalues[fc_i + 1];

		fc_a = construct_array(fc_dvalues, fc_arrayLength - 1,
							OIDOID, sizeof(Oid), true, TYPALIGN_INT);

		fc_repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(fc_a);
	}
	fc_repl_repl[Anum_pg_extension_extconfig - 1] = true;

	/* 修改或删除 extcondition 值 */
	fc_arrayDatum = heap_getattr(fc_extTup, Anum_pg_extension_extcondition,
							  RelationGetDescr(fc_extRel), &fc_isnull);
	if (fc_isnull)
	{
		elog(ERROR, "extconfig and extcondition arrays do not match");
	}
	else
	{
		fc_a = DatumGetArrayTypeP(fc_arrayDatum);

		if (ARR_NDIM(fc_a) != 1 ||
			ARR_LBOUND(fc_a)[0] != 1 ||
			ARR_HASNULL(fc_a) ||
			ARR_ELEMTYPE(fc_a) != TEXTOID)
			elog(ERROR, "extcondition is not a 1-D text array");
		if (ARR_DIMS(fc_a)[0] != fc_arrayLength)
			elog(ERROR, "extconfig and extcondition arrays do not match");
	}

	if (fc_arrayLength <= 1)
	{
		/* 仅移除元素，仅将数组设置为 null */
		fc_repl_null[Anum_pg_extension_extcondition - 1] = true;
	}
	else
	{
		/* 压缩目标元素 */
		Datum	   *fc_dvalues;
		int			fc_nelems;
		int			fc_i;

		/* 我们已经检查过没有空值 */
		deconstruct_array(fc_a, TEXTOID, -1, false, TYPALIGN_INT,
						  &fc_dvalues, NULL, &fc_nelems);

		for (fc_i = fc_arrayIndex; fc_i < fc_arrayLength - 1; fc_i++)
			fc_dvalues[fc_i] = fc_dvalues[fc_i + 1];

		fc_a = construct_array(fc_dvalues, fc_arrayLength - 1,
							TEXTOID, -1, false, TYPALIGN_INT);

		fc_repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(fc_a);
	}
	fc_repl_repl[Anum_pg_extension_extcondition - 1] = true;

	fc_extTup = heap_modify_tuple(fc_extTup, RelationGetDescr(fc_extRel),
							   fc_repl_val, fc_repl_null, fc_repl_repl);

	CatalogTupleUpdate(fc_extRel, &fc_extTup->t_self, fc_extTup);

	systable_endscan(fc_extScan);

	table_close(fc_extRel, RowExclusiveLock);
}

/* 
 * 执行 ALTER EXTENSION SET SCHEMA 
 */
ObjectAddress AlterExtensionNamespace(const char *fc_extensionName, const char *fc_newschema, Oid *fc_oldschema)
{
	Oid			fc_extensionOid;
	Oid			fc_nspOid;
	Oid			fc_oldNspOid;
	AclResult	fc_aclresult;
	Relation	fc_extRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_extScan;
	HeapTuple	fc_extTup;
	Form_pg_extension fc_extForm;
	Relation	fc_depRel;
	SysScanDesc fc_depScan;
	HeapTuple	fc_depTup;
	ObjectAddresses *fc_objsMoved;
	ObjectAddress fc_extAddr;

	fc_extensionOid = get_extension_oid(fc_extensionName, false);

	fc_nspOid = LookupCreationNamespace(fc_newschema);

	/* 
 * 权限检查：必须拥有扩展。请注意，我们不检查个别成员对象的所有权…… 
 */
	if (!pg_extension_ownercheck(fc_extensionOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
					   fc_extensionName);

	/* 权限检查：必须在目标命名空间中拥有创建权限 */
	fc_aclresult = pg_namespace_aclcheck(fc_nspOid, GetUserId(), ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA, fc_newschema);

	/* 
 * 如果当前 schema 是扩展的成员，则不允许将扩展移动到该 schema。这样会创建依赖循环。 
 */
	if (getExtensionOfObject(NamespaceRelationId, fc_nspOid) == fc_extensionOid)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("cannot move extension \"%s\" into schema \"%s\" "
						"because the extension contains the schema",
						fc_extensionName, fc_newschema)));

	/* 定位 pg_extension 元组 */
	fc_extRel = table_open(ExtensionRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_extensionOid));

	fc_extScan = systable_beginscan(fc_extRel, ExtensionOidIndexId, true,
								 NULL, 1, fc_key);

	fc_extTup = systable_getnext(fc_extScan);

	if (!HeapTupleIsValid(fc_extTup))	/* 不应该发生 */
		elog(ERROR, "could not find tuple for extension %u",
			 fc_extensionOid);

	/* 复制元组以便我们可以在下面修改它 */
	fc_extTup = heap_copytuple(fc_extTup);
	fc_extForm = (Form_pg_extension) GETSTRUCT(fc_extTup);

	systable_endscan(fc_extScan);

	/* 
 * 如果扩展已经在目标 schema 中，则静默不做任何操作。 
 */
	if (fc_extForm->extnamespace == fc_nspOid)
	{
		table_close(fc_extRel, RowExclusiveLock);
		return InvalidObjectAddress;
	}

	/* 检查扩展是否应可重新定位 */
	if (!fc_extForm->extrelocatable)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("extension \"%s\" does not support SET SCHEMA",
						NameStr(fc_extForm->extname))));

	fc_objsMoved = new_object_addresses();

	/* 存储将要更改的命名空间的 OID */
	fc_oldNspOid = fc_extForm->extnamespace;

	/* 
 * 扫描 pg_depend 以查找直接依赖于扩展的对象，并更改每个对象的 schema。
 */
	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_refclassid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(ExtensionRelationId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_refobjid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_extensionOid));

	fc_depScan = systable_beginscan(fc_depRel, DependReferenceIndexId, true,
								 NULL, 2, fc_key);

	while (HeapTupleIsValid(fc_depTup = systable_getnext(fc_depScan)))
	{
		Form_pg_depend fc_pg_depend = (Form_pg_depend) GETSTRUCT(fc_depTup);
		ObjectAddress fc_dep;
		Oid			fc_dep_oldNspOid;

		/* 
 * 忽略非成员依赖关系。（目前，我们在这里看到的唯一其他情况是来自另一个扩展的正常依赖关系。） 
 */
		if (fc_pg_depend->deptype != DEPENDENCY_EXTENSION)
			continue;

		fc_dep.classId = fc_pg_depend->classid;
		fc_dep.objectId = fc_pg_depend->objid;
		fc_dep.objectSubId = fc_pg_depend->objsubid;

		if (fc_dep.objectSubId != 0)	/* 不应该发生 */
			elog(ERROR, "extension should not have a sub-object dependency");

		/* 重新定位对象 */
		fc_dep_oldNspOid = AlterObjectNamespace_oid(fc_dep.classId,
												 fc_dep.objectId,
												 fc_nspOid,
												 fc_objsMoved);

		/* 
 * 如果并非所有对象都具有相同的旧命名空间（忽略任何不在命名空间中的对象），则抱怨。
 */
		if (fc_dep_oldNspOid != InvalidOid && fc_dep_oldNspOid != fc_oldNspOid)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("extension \"%s\" does not support SET SCHEMA",
							NameStr(fc_extForm->extname)),
					 errdetail("%s is not in the extension's schema \"%s\"",
							   getObjectDescription(&fc_dep, false),
							   get_namespace_name(fc_oldNspOid))));
	}

	/* 如果调用者需要，报告旧 schema */
	if (fc_oldschema)
		*fc_oldschema = fc_oldNspOid;

	systable_endscan(fc_depScan);

	relation_close(fc_depRel, AccessShareLock);

	/* 现在调整 pg_extension.extnamespace */
	fc_extForm->extnamespace = fc_nspOid;

	CatalogTupleUpdate(fc_extRel, &fc_extTup->t_self, fc_extTup);

	table_close(fc_extRel, RowExclusiveLock);

	/* 更新依赖项以指向新架构 */
	changeDependencyFor(ExtensionRelationId, fc_extensionOid,
						NamespaceRelationId, fc_oldNspOid, fc_nspOid);

	InvokeObjectPostAlterHook(ExtensionRelationId, fc_extensionOid, 0);

	ObjectAddressSet(fc_extAddr, ExtensionRelationId, fc_extensionOid);

	return fc_extAddr;
}

/* 
 * 执行 ALTER EXTENSION UPDATE 
 */
ObjectAddress ExecAlterExtensionStmt(ParseState *fc_pstate, AlterExtensionStmt *fc_stmt)
{
	DefElem    *fc_d_new_version = NULL;
	char	   *fc_versionName;
	char	   *fc_oldVersionName;
	ExtensionControlFile *fc_control;
	Oid			fc_extensionOid;
	Relation	fc_extRel;
	ScanKeyData fc_key[1];
	SysScanDesc fc_extScan;
	HeapTuple	fc_extTup;
	List	   *fc_updateVersions;
	Datum		fc_datum;
	bool		fc_isnull;
	ListCell   *fc_lc;
	ObjectAddress fc_address;

	/* 
 * 我们使用全局变量来跟踪正在创建的扩展，以便我们可以同时只创建/更新一个扩展。
 */
	if (creating_extension)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("nested ALTER EXTENSION is not supported")));

	/* 
 * 查找扩展 — 它必须已经存在于 pg_extension 中 
 */
	fc_extRel = table_open(ExtensionRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_extension_extname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_stmt->extname));

	fc_extScan = systable_beginscan(fc_extRel, ExtensionNameIndexId, true,
								 NULL, 1, fc_key);

	fc_extTup = systable_getnext(fc_extScan);

	if (!HeapTupleIsValid(fc_extTup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("extension \"%s\" does not exist",
						fc_stmt->extname)));

	fc_extensionOid = ((Form_pg_extension) GETSTRUCT(fc_extTup))->oid;

	/* 
 * 确定我们要更新的现有版本 
 */
	fc_datum = heap_getattr(fc_extTup, Anum_pg_extension_extversion,
						 RelationGetDescr(fc_extRel), &fc_isnull);
	if (fc_isnull)
		elog(ERROR, "extversion is null");
	fc_oldVersionName = text_to_cstring(DatumGetTextPP(fc_datum));

	systable_endscan(fc_extScan);

	table_close(fc_extRel, AccessShareLock);

	/* 权限检查：必须拥有扩展 */
	if (!pg_extension_ownercheck(fc_extensionOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
					   fc_stmt->extname);

	/*
	 * 读取主要控制文件。请注意，我们假设它不包含任何非 ASCII 数据，因此此时无需担心编码问题。
	 */
	fc_control = fc_read_extension_control_file(fc_stmt->extname);

	/* 
 * 读取语句选项列表 
 */
	foreach(fc_lc, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_lc);

		if (strcmp(fc_defel->defname, "new_version") == 0)
		{
			if (fc_d_new_version)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_d_new_version = fc_defel;
		}
		else
			elog(ERROR, "unrecognized option: %s", fc_defel->defname);
	}

	/* 
 * 确定要更新到的版本 
 */
	if (fc_d_new_version && fc_d_new_version->arg)
		fc_versionName = strVal(fc_d_new_version->arg);
	else if (fc_control->default_version)
		fc_versionName = fc_control->default_version;
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("version to install must be specified")));
		fc_versionName = NULL;		/* 保持编译器安静 */
#ifdef FDD //cppcheck
		//cppcheck
		return InvalidObjectAddress;
#endif
	}
	fc_check_valid_version_name(fc_versionName);

	/* 
 * 如果我们已经在该版本上，则只需说明 
 */
	if (strcmp(fc_oldVersionName, fc_versionName) == 0)
	{
		ereport(NOTICE,
				(errmsg("version \"%s\" of extension \"%s\" is already installed",
						fc_versionName, fc_stmt->extname)));
		return InvalidObjectAddress;
	}

	/* 
 * 确定我们需要执行的一系列更新脚本文件 
 */
	fc_updateVersions = fc_identify_update_path(fc_control,
										  fc_oldVersionName,
										  fc_versionName);

	/* 
 * 更新 pg_extension 行并依次执行更新脚本 
 */
	fc_ApplyExtensionUpdates(fc_extensionOid, fc_control,
						  fc_oldVersionName, fc_updateVersions,
						  NULL, false, false);

	ObjectAddressSet(fc_address, ExtensionRelationId, fc_extensionOid);

	return fc_address;
}

/* 
 * 应用一系列更新脚本，就好像单独的 ALTER EXTENSION 
 * UPDATE 命令已经给出，每次都包括更改 pg_extension 行和依赖关系。
 *
 * 这可能比必要的工作要多，但它确保旧的更新脚本不会因
 * 新版本具有不同的控制参数而中断。
 */
static void fc_ApplyExtensionUpdates(Oid fc_extensionOid,
					  ExtensionControlFile *fc_pcontrol,
					  const char *fc_initialVersion,
					  List *fc_updateVersions,
					  char *fc_origSchemaName,
					  bool fc_cascade,
					  bool fc_is_create)
{
	const char *fc_oldVersionName = fc_initialVersion;
	ListCell   *fc_lcv;

	foreach(fc_lcv, fc_updateVersions)
	{
		char	   *fc_versionName = (char *) lfirst(fc_lcv);
		ExtensionControlFile *fc_control;
		char	   *fc_schemaName;
		Oid			fc_schemaOid;
		List	   *fc_requiredExtensions;
		List	   *fc_requiredSchemas;
		Relation	fc_extRel;
		ScanKeyData fc_key[1];
		SysScanDesc fc_extScan;
		HeapTuple	fc_extTup;
		Form_pg_extension fc_extForm;
		Datum		fc_values[Natts_pg_extension];
		bool		fc_nulls[Natts_pg_extension];
		bool		fc_repl[Natts_pg_extension];
		ObjectAddress fc_myself;
		ListCell   *fc_lc;

		/*
		 * 获取特定版本的参数（pcontrol 不变）
		 */
		fc_control = fc_read_extension_aux_control_file(fc_pcontrol, fc_versionName);

		/* 找到 pg_extension 元组 */
		fc_extRel = table_open(ExtensionRelationId, RowExclusiveLock);

		ScanKeyInit(&fc_key[0],
					Anum_pg_extension_oid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_extensionOid));

		fc_extScan = systable_beginscan(fc_extRel, ExtensionOidIndexId, true,
									 NULL, 1, fc_key);

		fc_extTup = systable_getnext(fc_extScan);

		if (!HeapTupleIsValid(fc_extTup))	/* 不应该发生 */
			elog(ERROR, "could not find tuple for extension %u",
				 fc_extensionOid);

		fc_extForm = (Form_pg_extension) GETSTRUCT(fc_extTup);

		/*
		 * 确定目标模式（由原始安装设置）
		 */
		fc_schemaOid = fc_extForm->extnamespace;
		fc_schemaName = get_namespace_name(fc_schemaOid);

		/*
		 * 修改 pg_extension 元组中的 extrelocatable 和 extversion
		 */
		memset(fc_values, 0, sizeof(fc_values));
		memset(fc_nulls, 0, sizeof(fc_nulls));
		memset(fc_repl, 0, sizeof(fc_repl));

		fc_values[Anum_pg_extension_extrelocatable - 1] =
			BoolGetDatum(fc_control->relocatable);
		fc_repl[Anum_pg_extension_extrelocatable - 1] = true;
		fc_values[Anum_pg_extension_extversion - 1] =
			CStringGetTextDatum(fc_versionName);
		fc_repl[Anum_pg_extension_extversion - 1] = true;

		fc_extTup = heap_modify_tuple(fc_extTup, RelationGetDescr(fc_extRel),
								   fc_values, fc_nulls, fc_repl);

		CatalogTupleUpdate(fc_extRel, &fc_extTup->t_self, fc_extTup);

		systable_endscan(fc_extScan);

		table_close(fc_extRel, RowExclusiveLock);

		/*
		 * 查找此版本的先决条件扩展，如果必要，安装它们，并构建它们的 OID 及其目标模式的 OID 列表。
		 */
		fc_requiredExtensions = NIL;
		fc_requiredSchemas = NIL;
		foreach(fc_lc, fc_control->requires)
		{
			char	   *fc_curreq = (char *) lfirst(fc_lc);
			Oid			fc_reqext;
			Oid			fc_reqschema;

			fc_reqext = fc_get_required_extension(fc_curreq,
											fc_control->name,
											fc_origSchemaName,
											fc_cascade,
											NIL,
											fc_is_create);
			fc_reqschema = fc_get_extension_schema(fc_reqext);
			fc_requiredExtensions = lappend_oid(fc_requiredExtensions, fc_reqext);
			fc_requiredSchemas = lappend_oid(fc_requiredSchemas, fc_reqschema);
		}

		/*
		 * 移除并重新创建对先决条件扩展的依赖
		 */
		deleteDependencyRecordsForClass(ExtensionRelationId, fc_extensionOid,
										ExtensionRelationId,
										DEPENDENCY_NORMAL);

		fc_myself.classId = ExtensionRelationId;
		fc_myself.objectId = fc_extensionOid;
		fc_myself.objectSubId = 0;

		foreach(fc_lc, fc_requiredExtensions)
		{
			Oid			fc_reqext = lfirst_oid(fc_lc);
			ObjectAddress fc_otherext;

			fc_otherext.classId = ExtensionRelationId;
			fc_otherext.objectId = fc_reqext;
			fc_otherext.objectSubId = 0;

			recordDependencyOn(&fc_myself, &fc_otherext, DEPENDENCY_NORMAL);
		}

		InvokeObjectPostAlterHook(ExtensionRelationId, fc_extensionOid, 0);

		/*
		 * 最后，执行更新脚本文件
		 */
		fc_execute_extension_script(fc_extensionOid, fc_control,
								 fc_oldVersionName, fc_versionName,
								 fc_requiredSchemas,
								 fc_schemaName, fc_schemaOid);

		/*
		 * 更新先前版本名称并循环。由于
		 * execute_sql_string 进行了最终的 CommandCounterIncrement，我们可以
		 * 再次更新 pg_extension 行。
		 */
		fc_oldVersionName = fc_versionName;
	}
}

/*
 * 执行 ALTER EXTENSION 添加/删除
 *
 * 返回值是被更改扩展的地址。
 *
 * objAddr 是一个输出参数，如果不为 NULL，将设置为
 * 添加/删除对象的地址。
 */
ObjectAddress ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *fc_stmt,
							   ObjectAddress *fc_objAddr)
{
	ObjectAddress fc_extension;
	ObjectAddress fc_object;
	Relation	fc_relation;
	Oid			fc_oldExtension;

	switch (fc_stmt->objtype)
	{
		case OBJECT_DATABASE:
		case OBJECT_EXTENSION:
		case OBJECT_INDEX:
		case OBJECT_PUBLICATION:
		case OBJECT_ROLE:
		case OBJECT_STATISTIC_EXT:
		case OBJECT_SUBSCRIPTION:
		case OBJECT_TABLESPACE:
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("cannot add an object of this type to an extension")));
			break;
		default:
			/* 好的 */
			break;
	}

	/*
	 * 找到扩展并获取对其的锁，以确保不会被
	 * 并发删除。共享锁似乎足够：没有理由不允许其他类型的操作，例如同时添加/删除
	 * 其他对象。并发添加/删除
	 * *同一* 对象是不好的，但我们通过在下面对单个对象使用非共享锁来防止这种情况。
	 */
	fc_extension = get_object_address(OBJECT_EXTENSION,
								   (Node *) makeString(fc_stmt->extname),
								   &fc_relation, AccessShareLock, false);

	/* 权限检查：必须拥有扩展 */
	if (!pg_extension_ownercheck(fc_extension.objectId, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
					   fc_stmt->extname);

	/*
	 * 将识别对象的解析器表示转换为
	 * ObjectAddress。如果对象不存在，get_object_address() 将抛出错误，并且还会获取
	 * 对象的锁，以防止并发的 DROP 和 ALTER EXTENSION 添加/删除操作。
	 */
	fc_object = get_object_address(fc_stmt->objtype, fc_stmt->object,
								&fc_relation, ShareUpdateExclusiveLock, false);

	Assert(fc_object.objectSubId == 0);
	if (fc_objAddr)
		*fc_objAddr = fc_object;

	/* 权限检查：也必须拥有目标对象 */
	check_object_ownership(GetUserId(), fc_stmt->objtype, fc_object,
						   fc_stmt->object, fc_relation);

	/*
	 * 检查现有的扩展成员资格。
	 */
	fc_oldExtension = getExtensionOfObject(fc_object.classId, fc_object.objectId);

	if (fc_stmt->action > 0)
	{
		/*
		 * 如果对象已经附加到某个扩展，则添加将报错。
		 */
		if (OidIsValid(fc_oldExtension))
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("%s is already a member of extension \"%s\"",
							getObjectDescription(&fc_object, false),
							get_extension_name(fc_oldExtension))));

		/*
		 * 如果模式包含扩展，则防止将模式添加到扩展中。
		 * 那将创建一个依赖循环。
		 */
		if (fc_object.classId == NamespaceRelationId &&
			fc_object.objectId == fc_get_extension_schema(fc_extension.objectId))
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("cannot add schema \"%s\" to extension \"%s\" "
							"because the schema contains the extension",
							get_namespace_name(fc_object.objectId),
							fc_stmt->extname)));

		/*
		 * 好的，添加依赖。
		 */
		recordDependencyOn(&fc_object, &fc_extension, DEPENDENCY_EXTENSION);

		/*
		 * 如果有，记录对象的初始 ACL。
		 *
		 * 请注意，这将处理对象的 ACL，以及任何对象子 ID 的 ACL。
		 * （换句话说，当对象是表时，这将记录表的 ACL 和列的 ACL，
		 * 如果有的话）。
		 */
		recordExtObjInitPriv(fc_object.objectId, fc_object.classId);
	}
	else
	{
		/*
		 * 删除，因此如果它不是成员，则报错。
		 */
		if (fc_oldExtension != fc_extension.objectId)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("%s is not a member of extension \"%s\"",
							getObjectDescription(&fc_object, false),
							fc_stmt->extname)));

		/*
		 * 好的，删除依赖。
		 */
		if (deleteDependencyRecordsForClass(fc_object.classId, fc_object.objectId,
											ExtensionRelationId,
											DEPENDENCY_EXTENSION) != 1)
			elog(ERROR, "unexpected number of extension dependency records");

		/*
		 * 如果是关系，它可能在扩展的
		 * extconfig 数组中有一个条目，我们必须删除它。
		 */
		if (fc_object.classId == RelationRelationId)
			fc_extension_config_remove(fc_extension.objectId, fc_object.objectId);

		/*
		 * 删除所有初始 ACL（如果有的话）。
		 *
		 * 请注意，这将删除对象的 ACL，以及任何对象子 ID 的 ACL。
		 * （换句话说，当对象是表时，这将删除表的 ACL 和列的 ACL，
		 * 如果有的话）。
		 */
		removeExtObjInitPriv(fc_object.objectId, fc_object.classId);
	}

	InvokeObjectPostAlterHook(ExtensionRelationId, fc_extension.objectId, 0);

	/*
	 * 如果 get_object_address() 为我们打开了关系，
	 * 我们会关闭它以保持引用计数的正确性
	 * - 但我们会在提交时保留 get_object_address() 获取的任何锁，
	 * 以防止并发活动。
	 */
	if (fc_relation != NULL)
		relation_close(fc_relation, NoLock);

	return fc_extension;
}

/*
 * 将整个文件读取到内存中。
 *
 * 文件内容作为一个单独的 palloc'd 块返回。为了方便调用者，末尾添加了一个额外的 \0 字节。
 */
static char * fc_read_whole_file(const char *fc_filename, int *fc_length)
{
	char	   *fc_buf;
	FILE	   *fc_file;
	size_t		fc_bytes_to_read;
	struct stat fc_fst;

	if (stat(fc_filename, &fc_fst) < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not stat file \"%s\": %m", fc_filename)));

	if (fc_fst.st_size > (MaxAllocSize - 1))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("file \"%s\" is too large", fc_filename)));
	fc_bytes_to_read = (size_t) fc_fst.st_size;

	if ((fc_file = AllocateFile(fc_filename, PG_BINARY_R)) == NULL)
#ifdef FDD //cppcheck
	{//cppcheck
#endif
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open file \"%s\" for reading: %m",
						fc_filename)));
#ifdef FDD //cppcheck
		return NULL;
	}
#endif

	fc_buf = (char *) palloc(fc_bytes_to_read + 1);

	*fc_length = fread(fc_buf, 1, fc_bytes_to_read, fc_file);

	if (ferror(fc_file))
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not read file \"%s\": %m", fc_filename)));

	FreeFile(fc_file);

	fc_buf[*fc_length] = '\0';
	return fc_buf;
}
